laya-cli 0.2.0__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: laya-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Ergonomic CLI for Laya — typed decisions for humans and AI agents (predict, batch, presets, shortlist, router)
5
5
  Project-URL: Homepage, https://github.com/MIt9/laya-cli
6
6
  Project-URL: Repository, https://github.com/MIt9/laya-cli.git
@@ -51,6 +51,7 @@ A modern, high-performance CLI for [Laya](https://github.com/NandhaKishorM/laya)
51
51
  * 🔍 **Preset Library**: `triage` / `email` / `guard` / `moderation` / `router` — direct `laya.*_questions()` passthrough, mergeable with `--questions file.json` and `--questions-inline`
52
52
  * 📦 **Streaming Batch Mode**: `classify`/`predict --input` loads model **once** + warmup, streams JSONL (`--state-field` verbatim, no silent `(photographer: Name)` injection)
53
53
  * 🔧 **Post-Filter & Eval**: `filter --where "on_topic>=0.4" --sort -on_topic` and `evaluate` (accuracy, passing @ threshold, precision, escalation rate)
54
+ * ⚡ **Resident Daemon (optional)**: `laya-cli serve` keeps model in RAM — `predict`/`classify`/`evaluate` auto-hit `127.0.0.1` and skip 10-35s `laya.load()` on repeated calls (hash → `~/.cache/laya-cli/daemons/<hash>.json`, idle-timeout 1800s)
54
55
 
55
56
  ---
56
57
 
@@ -160,32 +161,68 @@ laya-cli evaluate --questions questions.json --labeled labeled.jsonl --label-fie
160
161
 
161
162
  ### 4. Resident Daemon Mode (optional, speeds up repeated calls)
162
163
 
163
- Measured: `laya.load()` 10–35s (MPS) + ~120ms × 82 candidates. For one pipeline run it's fine; for iterative tuning of `questions.json` each `predict` pays 10–35s again. `serve` keeps the model in RAM — subsequent calls skip the load and hit `127.0.0.1` (<1ms overhead).
164
+ **Виміряний факт (сесія 2026-09-22, 82 кандидати, MPS):**
165
+
166
+ | етап | час |
167
+ |------|-----|
168
+ | `px` (мережа, 12 запитів) | 9.3s |
169
+ | `laya.load()` (модель у пам'ять) | 10–35s |
170
+ | inference, 82 кандидати | ~10s (~120ms/шт) |
171
+ | `filter` | 0.08s |
172
+
173
+ Для одного пайплайну `px → classify → filter` 35–50s норм. Проблема — коли за сесію кілька разів викликаєш `predict`/`classify`/`evaluate` (підбір `questions.json`: прогнав → подивився → поправив → знову), кожен раз платиш 10–35s за ту саму модель. `laya-cli serve` тримає модель в RAM, наступні виклики летять на `127.0.0.1` (<1ms overhead).
174
+
175
+ #### Транспорт (як у `~/.claude/skills/laya-integration/SKILL.md` "Anything else... HTTP sidecar")
176
+
177
+ HTTP на loopback `127.0.0.1` (не `0.0.0.0`), один потік з `threading.Lock` (SKILL.md: "one GPU serves one forward pass at a time"):
178
+
179
+ - `POST /predict` → `{"state": ..., "questions": {...}, "lang": "...", "shortlist_k": 20}` → те саме що `agent.predict()` (той самий JSON що `laya-cli predict --format json`)
180
+ - `GET /status` → `{"model": "...", "subfolder": "...", "device": "...", "loaded_at": 123..., "idle_seconds": 42, "requests_served": 17, "pid": 12345, "port": 8765}`
181
+ - `POST /shutdown` → graceful stop (лише з localhost, також `serve stop` шле сигнал за pid-файлом)
182
+
183
+ #### Lifecycle
164
184
 
165
185
  ```bash
166
- # Start daemon in background (one daemon per model/device/router config)
186
+ # Старт (без --foreground форк у фон, пише pid+port в ~/.cache/laya-cli/daemons/<hash>.json)
167
187
  laya-cli serve --model convaiinnovations/laya --device mps
168
- laya-cli serve --model convaiinnovations/laya --subfolder multilingual --device cpu --idle-timeout 60 # short idle for test
169
- laya-cli serve --router --foreground --idle-timeout 0 # foreground, no auto-exit, for logs
188
+ laya-cli serve --model convaiinnovations/laya --subfolder multilingual --device cpu --idle-timeout 60
189
+ laya-cli serve --router --foreground --idle-timeout 0 --port 8765 # форграунд для логів, 0=disabled idle
190
+
191
+ # Один daemon = один конфіг (hash(model|subfolder|device|router|lang)), інший конфіг — окремий файл/порт
192
+ laya-cli serve status # дефолтний конфіг (як у predict без прапорців)
193
+ laya-cli serve status --model convaiinnovations/laya --subfolder multilingual --device cpu
194
+ laya-cli serve stop # graceful (POST /shutdown → pid файл видаляється)
195
+ laya-cli serve stop --all # всі daemon-и
196
+ # Також: curl http://127.0.0.1:<port>/status
197
+ ```
170
198
 
171
- # Check status (also shows port, pid, idle_seconds, requests_served)
172
- laya-cli serve status
173
- laya-cli serve status --model convaiinnovations/laya --subfolder multilingual
199
+ Перед прийомом запитів — прогрів throwaway `predict` (як у `TASK.md`). `idle-timeout 1800s` дефолт, `--idle-timeout 5` для тесту → daemon сам виходить через 5s без запитів і `serve status` каже `not running`.
174
200
 
175
- # Use it — predict/classify/evaluate automatically hit the daemon if live for that config
176
- laya-cli predict "hello" --preset guard --format json # -> via daemon, no 10s load
177
- laya-cli predict "hello" --preset guard --no-daemon --format json # force in-process, ignore daemon
201
+ #### Клієнт (`predict`/`classify`/`evaluate`)
178
202
 
179
- # Batch also benefits (each line -> POST /predict on loopback, serialized with lock)
180
- cat candidates.jsonl | laya-cli classify --questions q.json | laya-cli filter --where "on_topic>=0.4" --sort -on_topic
203
+ Перед `laya.load()` перевіряє `~/.cache/laya-cli/daemons/<hash>.json` для поточного конфігу; якщо файл є і daemon відповідає на `/status` — шле туди, інакше мовчки падає назад на in-process (поведінка `v1` без змін). `--no-daemon` форсує in-process (для відтворюваності/дебагу). Для батчу (`classify`/`predict --input`) — кожен рядок окремий `POST /predict` в циклі (loopback мілісекунди, batch-ендпоінт не потрібен).
181
204
 
182
- # Stop
183
- laya-cli serve stop # stop daemon for default config
184
- laya-cli serve stop --all # stop all daemons
185
- curl http://127.0.0.1:<port>/status # GET /status, POST /predict, POST /shutdown also work directly
205
+ ```bash
206
+ # AI workflow (повністю автоматичний):
207
+ laya-cli serve --device mps & # один раз на сесію
208
+ laya-cli predict "hello" --preset guard --format json # -> via daemon, без 10s
209
+ laya-cli predict "hello2" --preset guard --format json # -> знову via daemon
210
+ laya-cli serve status # {"loaded_at":..., "requests_served": 2}
211
+ laya-cli predict "hello" --preset guard --no-daemon # форс in-process (знову 10s, для ізоляції)
212
+ laya-cli serve stop
213
+
214
+ # Human tuning loop (типовий):
215
+ laya-cli serve & # фон
216
+ cat candidates.jsonl | laya-cli classify --questions q.json | laya-cli filter --where "on_topic>=0.4" --sort -on_topic # via daemon
217
+ # ...поправив q.json...
218
+ cat candidates.jsonl | laya-cli classify --questions q.json | laya-cli filter ... # знову via daemon, без перезавантаження
219
+ laya-cli serve stop
220
+
221
+ # 5 паралельних predict — не падають, результати не плутаються (серіалізація Lock)
222
+ seq 1 5 | xargs -P5 -I{} laya-cli predict "text {}" --preset guard --format json
186
223
  ```
187
224
 
188
- **Details:** HTTP on `127.0.0.1` only (SKILL.md: "Bind to 127.0.0.1"), `POST /predict {"state":..., "questions":{...}}` → same as `agent.predict()`, `GET /status` `{model, device, loaded_at, idle_seconds, requests_served}`, `POST /shutdown` (localhost only). One daemon = one checkpoint hash(`model|subfolder|device|router|lang`) → pid+port in `~/.cache/laya-cli/daemons/<hash>.json`, pid file removed on exit. Idle timeout 1800s default, `0` disables. If daemon not running, `predict`/`classify`/`evaluate` silently fall back to in-process load — no new step for scripts. `--no-daemon` forces fallback. Requests are serialized with a `threading.Lock` (one GPU = one forward pass).
225
+ **Безпека/межі:** лише `127.0.0.1`, без auth (однокористувацька машина, як у SKILL.md), один потік, stateless крім моделі.
189
226
 
190
227
  ---
191
228
 
@@ -16,6 +16,7 @@ A modern, high-performance CLI for [Laya](https://github.com/NandhaKishorM/laya)
16
16
  * 🔍 **Preset Library**: `triage` / `email` / `guard` / `moderation` / `router` — direct `laya.*_questions()` passthrough, mergeable with `--questions file.json` and `--questions-inline`
17
17
  * 📦 **Streaming Batch Mode**: `classify`/`predict --input` loads model **once** + warmup, streams JSONL (`--state-field` verbatim, no silent `(photographer: Name)` injection)
18
18
  * 🔧 **Post-Filter & Eval**: `filter --where "on_topic>=0.4" --sort -on_topic` and `evaluate` (accuracy, passing @ threshold, precision, escalation rate)
19
+ * ⚡ **Resident Daemon (optional)**: `laya-cli serve` keeps model in RAM — `predict`/`classify`/`evaluate` auto-hit `127.0.0.1` and skip 10-35s `laya.load()` on repeated calls (hash → `~/.cache/laya-cli/daemons/<hash>.json`, idle-timeout 1800s)
19
20
 
20
21
  ---
21
22
 
@@ -125,32 +126,68 @@ laya-cli evaluate --questions questions.json --labeled labeled.jsonl --label-fie
125
126
 
126
127
  ### 4. Resident Daemon Mode (optional, speeds up repeated calls)
127
128
 
128
- Measured: `laya.load()` 10–35s (MPS) + ~120ms × 82 candidates. For one pipeline run it's fine; for iterative tuning of `questions.json` each `predict` pays 10–35s again. `serve` keeps the model in RAM — subsequent calls skip the load and hit `127.0.0.1` (<1ms overhead).
129
+ **Виміряний факт (сесія 2026-09-22, 82 кандидати, MPS):**
130
+
131
+ | етап | час |
132
+ |------|-----|
133
+ | `px` (мережа, 12 запитів) | 9.3s |
134
+ | `laya.load()` (модель у пам'ять) | 10–35s |
135
+ | inference, 82 кандидати | ~10s (~120ms/шт) |
136
+ | `filter` | 0.08s |
137
+
138
+ Для одного пайплайну `px → classify → filter` 35–50s норм. Проблема — коли за сесію кілька разів викликаєш `predict`/`classify`/`evaluate` (підбір `questions.json`: прогнав → подивився → поправив → знову), кожен раз платиш 10–35s за ту саму модель. `laya-cli serve` тримає модель в RAM, наступні виклики летять на `127.0.0.1` (<1ms overhead).
139
+
140
+ #### Транспорт (як у `~/.claude/skills/laya-integration/SKILL.md` "Anything else... HTTP sidecar")
141
+
142
+ HTTP на loopback `127.0.0.1` (не `0.0.0.0`), один потік з `threading.Lock` (SKILL.md: "one GPU serves one forward pass at a time"):
143
+
144
+ - `POST /predict` → `{"state": ..., "questions": {...}, "lang": "...", "shortlist_k": 20}` → те саме що `agent.predict()` (той самий JSON що `laya-cli predict --format json`)
145
+ - `GET /status` → `{"model": "...", "subfolder": "...", "device": "...", "loaded_at": 123..., "idle_seconds": 42, "requests_served": 17, "pid": 12345, "port": 8765}`
146
+ - `POST /shutdown` → graceful stop (лише з localhost, також `serve stop` шле сигнал за pid-файлом)
147
+
148
+ #### Lifecycle
129
149
 
130
150
  ```bash
131
- # Start daemon in background (one daemon per model/device/router config)
151
+ # Старт (без --foreground форк у фон, пише pid+port в ~/.cache/laya-cli/daemons/<hash>.json)
132
152
  laya-cli serve --model convaiinnovations/laya --device mps
133
- laya-cli serve --model convaiinnovations/laya --subfolder multilingual --device cpu --idle-timeout 60 # short idle for test
134
- laya-cli serve --router --foreground --idle-timeout 0 # foreground, no auto-exit, for logs
153
+ laya-cli serve --model convaiinnovations/laya --subfolder multilingual --device cpu --idle-timeout 60
154
+ laya-cli serve --router --foreground --idle-timeout 0 --port 8765 # форграунд для логів, 0=disabled idle
155
+
156
+ # Один daemon = один конфіг (hash(model|subfolder|device|router|lang)), інший конфіг — окремий файл/порт
157
+ laya-cli serve status # дефолтний конфіг (як у predict без прапорців)
158
+ laya-cli serve status --model convaiinnovations/laya --subfolder multilingual --device cpu
159
+ laya-cli serve stop # graceful (POST /shutdown → pid файл видаляється)
160
+ laya-cli serve stop --all # всі daemon-и
161
+ # Також: curl http://127.0.0.1:<port>/status
162
+ ```
135
163
 
136
- # Check status (also shows port, pid, idle_seconds, requests_served)
137
- laya-cli serve status
138
- laya-cli serve status --model convaiinnovations/laya --subfolder multilingual
164
+ Перед прийомом запитів — прогрів throwaway `predict` (як у `TASK.md`). `idle-timeout 1800s` дефолт, `--idle-timeout 5` для тесту → daemon сам виходить через 5s без запитів і `serve status` каже `not running`.
139
165
 
140
- # Use it — predict/classify/evaluate automatically hit the daemon if live for that config
141
- laya-cli predict "hello" --preset guard --format json # -> via daemon, no 10s load
142
- laya-cli predict "hello" --preset guard --no-daemon --format json # force in-process, ignore daemon
166
+ #### Клієнт (`predict`/`classify`/`evaluate`)
143
167
 
144
- # Batch also benefits (each line -> POST /predict on loopback, serialized with lock)
145
- cat candidates.jsonl | laya-cli classify --questions q.json | laya-cli filter --where "on_topic>=0.4" --sort -on_topic
168
+ Перед `laya.load()` перевіряє `~/.cache/laya-cli/daemons/<hash>.json` для поточного конфігу; якщо файл є і daemon відповідає на `/status` — шле туди, інакше мовчки падає назад на in-process (поведінка `v1` без змін). `--no-daemon` форсує in-process (для відтворюваності/дебагу). Для батчу (`classify`/`predict --input`) — кожен рядок окремий `POST /predict` в циклі (loopback мілісекунди, batch-ендпоінт не потрібен).
146
169
 
147
- # Stop
148
- laya-cli serve stop # stop daemon for default config
149
- laya-cli serve stop --all # stop all daemons
150
- curl http://127.0.0.1:<port>/status # GET /status, POST /predict, POST /shutdown also work directly
170
+ ```bash
171
+ # AI workflow (повністю автоматичний):
172
+ laya-cli serve --device mps & # один раз на сесію
173
+ laya-cli predict "hello" --preset guard --format json # -> via daemon, без 10s
174
+ laya-cli predict "hello2" --preset guard --format json # -> знову via daemon
175
+ laya-cli serve status # {"loaded_at":..., "requests_served": 2}
176
+ laya-cli predict "hello" --preset guard --no-daemon # форс in-process (знову 10s, для ізоляції)
177
+ laya-cli serve stop
178
+
179
+ # Human tuning loop (типовий):
180
+ laya-cli serve & # фон
181
+ cat candidates.jsonl | laya-cli classify --questions q.json | laya-cli filter --where "on_topic>=0.4" --sort -on_topic # via daemon
182
+ # ...поправив q.json...
183
+ cat candidates.jsonl | laya-cli classify --questions q.json | laya-cli filter ... # знову via daemon, без перезавантаження
184
+ laya-cli serve stop
185
+
186
+ # 5 паралельних predict — не падають, результати не плутаються (серіалізація Lock)
187
+ seq 1 5 | xargs -P5 -I{} laya-cli predict "text {}" --preset guard --format json
151
188
  ```
152
189
 
153
- **Details:** HTTP on `127.0.0.1` only (SKILL.md: "Bind to 127.0.0.1"), `POST /predict {"state":..., "questions":{...}}` → same as `agent.predict()`, `GET /status` `{model, device, loaded_at, idle_seconds, requests_served}`, `POST /shutdown` (localhost only). One daemon = one checkpoint hash(`model|subfolder|device|router|lang`) → pid+port in `~/.cache/laya-cli/daemons/<hash>.json`, pid file removed on exit. Idle timeout 1800s default, `0` disables. If daemon not running, `predict`/`classify`/`evaluate` silently fall back to in-process load — no new step for scripts. `--no-daemon` forces fallback. Requests are serialized with a `threading.Lock` (one GPU = one forward pass).
190
+ **Безпека/межі:** лише `127.0.0.1`, без auth (однокористувацька машина, як у SKILL.md), один потік, stateless крім моделі.
154
191
 
155
192
  ---
156
193
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "laya-cli"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "Ergonomic CLI for Laya — typed decisions for humans and AI agents (predict, batch, presets, shortlist, router)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,3 +1,3 @@
1
1
  """laya-cli — streaming JSONL classifier over Laya."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.1"
@@ -53,7 +53,11 @@ except ImportError:
53
53
  def _build_parser() -> argparse.ArgumentParser:
54
54
  p = argparse.ArgumentParser(
55
55
  prog="laya-cli",
56
- description="Ergonomic CLI for Laya — typed decisions (choice/score/noul) in one forward pass. Works for humans (table output) and agents (JSON/JSONL).",
56
+ description=(
57
+ "Ergonomic CLI for Laya — typed decisions (choice/score/noul) in one forward pass. "
58
+ "Works for humans (table) and agents (JSON/JSONL). "
59
+ "Optional resident daemon (laya-cli serve) keeps model in RAM to avoid 10-35s laya.load() on repeated predict/classify/evaluate (same model|subfolder|device|router|lang → ~/.cache/laya-cli/daemons/<hash>.json, 127.0.0.1 only, auto fallback if not running)."
60
+ ),
57
61
  )
58
62
  p.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
59
63
  sub = p.add_subparsers(dest="cmd", required=True)
@@ -61,8 +65,11 @@ def _build_parser() -> argparse.ArgumentParser:
61
65
  # ---- predict (primary, human+AI friendly) -----------------------------
62
66
  pr = sub.add_parser(
63
67
  "predict",
64
- help="Run Laya on one text/JSON state or a batch (JSONL). Human-friendly & agent-friendly.",
65
- description="Predict with Laya on a single state or a batch. Supports plain text, JSON state, presets, shortlist for high-cardinality choices, and table/JSON output.",
68
+ help="Run Laya on one text/JSON state or a batch (JSONL). Human-friendly & agent-friendly. Auto-uses daemon if live.",
69
+ description=(
70
+ "Predict with Laya on a single state or a batch. Supports plain text, JSON state, presets, shortlist for high-cardinality choices, and table/JSON output. "
71
+ "If a daemon is live for the same --model/--subfolder/--device/--router/--lang (hash → ~/.cache/laya-cli/daemons/<hash>.json), predict hits 127.0.0.1:<port>/predict and skips 10-35s laya.load(); otherwise it loads in-process. Use --no-daemon to force in-process."
72
+ ),
66
73
  epilog=(
67
74
  "Examples (human):\n"
68
75
  ' laya-cli predict "I was charged twice, refund please" --preset triage\n'
@@ -74,6 +81,13 @@ def _build_parser() -> argparse.ArgumentParser:
74
81
  " cat candidates.jsonl | laya-cli predict --questions q.json --state-field state --format jsonl > scored.jsonl\n"
75
82
  " laya-cli predict --input candidates.jsonl --questions q.json --shortlist-k 20 --format jsonl\n"
76
83
  ' laya-cli predict --preset triage --text "my payment failed" --model convaiinnovations/laya --device cpu --full-probs\n'
84
+ "\n"
85
+ "Daemon (optional, speeds up repeated calls):\n"
86
+ " laya-cli serve --model convaiinnovations/laya --device mps # start daemon (background, writes ~/.cache/laya-cli/daemons/<hash>.json)\n"
87
+ " laya-cli serve status # check live daemon (GET /status)\n"
88
+ ' laya-cli predict "hello" --preset guard --format json # auto-uses daemon (<1ms overhead), no 10s load\n'
89
+ ' laya-cli predict "hello" --preset guard --no-daemon # force in-process, ignore daemon\n'
90
+ " laya-cli serve stop # graceful shutdown (POST /shutdown)\n"
77
91
  ),
78
92
  formatter_class=argparse.RawDescriptionHelpFormatter,
79
93
  )
@@ -190,14 +204,19 @@ def _build_parser() -> argparse.ArgumentParser:
190
204
  # ---- classify (legacy batch, kept for TASK.md compat) -----------------
191
205
  c = sub.add_parser(
192
206
  "classify",
193
- help="Batch classify JSONL from stdin via Laya (legacy, use predict for new code).",
194
- description="Classify JSONL from stdin via Laya; append flattened answers. Kept for pipeline compatibility (px | laya-cli classify). For new code prefer `predict`.",
207
+ help="Batch classify JSONL from stdin via Laya (legacy, use predict for new code). Auto-uses daemon if live.",
208
+ description=(
209
+ "Classify JSONL from stdin via Laya; append flattened answers. Kept for pipeline compatibility (px | laya-cli classify). "
210
+ "For new code prefer `predict`. If a daemon is live for the same config (hash → ~/.cache/laya-cli/daemons/<hash>.json), classify hits 127.0.0.1 and skips load; else in-process. Use --no-daemon to force."
211
+ ),
195
212
  epilog=(
196
213
  "Examples:\n"
197
214
  " cat candidates.jsonl | laya-cli classify --questions questions.json > scored.jsonl\n"
198
215
  ' px videos --queries "..." --state --dedupe keep-first \\\n'
199
216
  " | laya-cli classify --questions questions.json \\\n"
200
217
  ' | laya-cli filter --where "on_topic>=0.4" --sort -on_topic > shortlist.jsonl\n'
218
+ " # with daemon (start once, then all classify hit daemon):\n"
219
+ " laya-cli serve --model convaiinnovations/laya & laya-cli classify --questions q.json < candidates.jsonl\n"
201
220
  ),
202
221
  formatter_class=argparse.RawDescriptionHelpFormatter,
203
222
  )
@@ -279,12 +298,16 @@ def _build_parser() -> argparse.ArgumentParser:
279
298
  # ---- evaluate ---------------------------------------------------------
280
299
  e = sub.add_parser(
281
300
  "evaluate",
282
- help="Evaluate on a labelled JSONL set and print accuracy / threshold report.",
283
- description="Evaluate before shipping: run classify on a labelled set and report accuracy per question, pass rate at threshold, and precision @ threshold.",
301
+ help="Evaluate on a labelled JSONL set and print accuracy / threshold report. Auto-uses daemon if live.",
302
+ description=(
303
+ "Evaluate before shipping: run classify on a labelled set and report accuracy per question, pass rate at threshold, and precision @ threshold. "
304
+ "If a daemon is live for the same --model/--device/--router, evaluate hits it and skips load; else in-process. Use --no-daemon to force."
305
+ ),
284
306
  epilog=(
285
307
  "Examples:\n"
286
308
  " laya-cli evaluate --questions questions.json --labeled labeled.jsonl --label-field label --threshold 0.5\n"
287
309
  " laya-cli evaluate --questions q.json --labeled dev.jsonl --field on_topic --state-field state\n"
310
+ " laya-cli serve & laya-cli evaluate --questions q.json --labeled dev.jsonl # via daemon\n"
288
311
  ),
289
312
  formatter_class=argparse.RawDescriptionHelpFormatter,
290
313
  )
@@ -314,16 +337,28 @@ def _build_parser() -> argparse.ArgumentParser:
314
337
  s = sub.add_parser(
315
338
  "serve",
316
339
  help="Resident daemon that keeps model in memory (optional, speeds up repeated predict).",
317
- description="Start a resident daemon that holds the Laya model in RAM. Subsequent predict/classify/evaluate automatically use it if live (same model/device/router). No change to pipelines if daemon not running.",
340
+ description=(
341
+ "Start a resident daemon that holds the Laya model in RAM (10-35s laya.load() once). "
342
+ "Subsequent `predict`/`classify`/`evaluate` with the SAME config (--model/--subfolder/--device/--router/--lang → hash → ~/.cache/laya-cli/daemons/<hash>.json) "
343
+ "automatically hit 127.0.0.1:<port>/predict and skip the load (<1ms overhead). If no daemon, they fall back to in-process load — no pipeline change. "
344
+ "Daemon binds only 127.0.0.1 (SKILL.md: Bind to 127.0.0.1), single-threaded lock (one GPU = one forward pass), idle-timeout auto-exit (default 1800s, 0=disabled), warmup throwaway predict on start."
345
+ ),
318
346
  epilog=(
319
- "Examples:\n"
320
- " laya-cli serve --model convaiinnovations/laya --device mps # start daemon (background)\n"
321
- " laya-cli serve --foreground --idle-timeout 60 # foreground, for debugging\n"
322
- " laya-cli serve status # show live daemon\n"
323
- " laya-cli serve status --model convaiinnovations/laya --subfolder multilingual\n"
324
- " laya-cli serve stop # graceful shutdown\n"
325
- " laya-cli serve stop --all # stop all daemons\n"
326
- ' laya-cli predict "hello" --preset guard --no-daemon # force in-process, ignore daemon\n'
347
+ "Workflow for AI/human (typical tuning loop):\n"
348
+ " laya-cli serve --model convaiinnovations/laya --device mps # start once (background, writes ~/.cache/laya-cli/daemons/<hash>.json)\n"
349
+ " laya-cli serve status # GET /status → {model,device,loaded_at,idle_seconds,requests_served,pid,port}\n"
350
+ ' laya-cli predict "test" --preset triage --format json # auto via daemon (no 10s load)\n'
351
+ ' laya-cli predict "test2" --preset triage --format json # still via daemon\n'
352
+ ' laya-cli predict "test" --preset triage --no-daemon --format json # force in-process (ignore daemon)\n'
353
+ " cat candidates.jsonl | laya-cli classify --questions q.json # batch: each line POST /predict (serialized)\n"
354
+ " laya-cli serve stop # POST /shutdown or SIGTERM, removes pid file\n"
355
+ " laya-cli serve stop --all # stop all configs\n"
356
+ "\n"
357
+ "Other examples:\n"
358
+ " laya-cli serve --foreground --idle-timeout 5 --port 0 # foreground for logs, 5s idle test\n"
359
+ " laya-cli serve --model convaiinnovations/laya --subfolder multilingual --device cpu\n"
360
+ " laya-cli serve status --model convaiinnovations/laya --subfolder multilingual # check that config\n"
361
+ ' curl http://127.0.0.1:<port>/status ; curl -X POST http://127.0.0.1:<port>/predict -d \'{"state":"hi","questions":{...}}\'\n'
327
362
  ),
328
363
  formatter_class=argparse.RawDescriptionHelpFormatter,
329
364
  )
@@ -499,7 +499,7 @@ wheels = [
499
499
 
500
500
  [[package]]
501
501
  name = "laya-cli"
502
- version = "0.2.0"
502
+ version = "0.2.1"
503
503
  source = { editable = "." }
504
504
  dependencies = [
505
505
  { name = "huggingface-hub" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes