- Embed docs/assets/dashboard.png in both READMEs right after the dashboard intro so the demo is visible without running the service. - Describe the left rail (sessions, prompts, levels) and the main panel (per-call question, axes, verdict, SSE updates). - Refresh the License section: the repository now ships Apache-2.0. Co-Authored-By: Claude Code <noreply@anthropic.com>
Laya Router Hook
A local Laya multilingual service that scores each coding prompt before Codex or Claude Code sees it. The hook adds a System-1 hint: task level, and whether the agent should plan, read the repo, look up docs, or reason carefully.
It does not switch models. Codex and Claude Code keep the model already configured for that session.
Prompt
-> UserPromptSubmit hook (stdlib only, fail-open)
-> http://127.0.0.1:8765
-> Laya multilingual
-> additionalContext
-> Codex / Claude Code
A browser dashboard at http://127.0.0.1:8765/ shows each decision, grouped by session, and updates over server-sent events.
The left rail lists sessions with their latest prompt, client, infer count, and the System-1 level tag. The main panel expands one session across calls; each call shows the question that was asked, the per-axis probabilities, and the model's plain-language verdict. SSE pushes new rows without a refresh.
Cursor loads Claude Code hooks, but its prompt event cannot inject context. The hook sees CURSOR_VERSION and returns immediately.
Install
macOS, uv, and Python 3.10+.
./install.sh
The script:
- Creates
~/.laya-router/.venvand installs this repo into it - Downloads the
multilingualweights ofconvaiinnovations/laya - Registers a launchd agent,
com.laya-router, that starts at login and restarts if it exits - Writes a hook when Codex or Claude Code is already installed
The install is a normal copy, not an editable install. launchd blocks forever if it has to import a checkout under ~/Documents. laya-router restart copies the repo into the venv again and restarts the service.
One client only:
./install.sh --only codex
./install.sh --only claude
The first Codex session must trust the hook. Start codex and run /hooks. Editing the Python files does not change the command string, so Codex does not ask for review again.
Commands
After install, the binaries live in ~/.laya-router/.venv/bin.
laya-router status
laya-router logs
laya-router logs -f
laya-router route "Rename the variables in this function"
laya-router restart
laya-router stop
laya-router start
Remove the hooks and the launchd agent, and keep the runtime directory:
./uninstall.sh
Also delete config, logs, and the virtualenv:
./uninstall.sh --purge
Configuration
~/.laya-router/config.toml is written on first install and is not overwritten later.
| Key | Meaning |
|---|---|
host |
Bind address. Keep 127.0.0.1. |
port |
Default 8765. |
device |
auto, mps, cuda, or cpu. auto falls through if warmup fails. |
log_prompts |
Write the raw prompt into data/routes.jsonl. |
request_timeout |
Seconds the hook waits for the service. Default 2.5. |
max_prompt_chars |
Max characters sent to the model. Longer prompts keep the head and tail. |
Restart after config or server changes: laya-router restart.
How a decision is made
One forward pass asks five questions:
task_level:simple,normal, orcomplexneeds_plan,needs_repo,needs_web,needs_deep_reasoning: probabilities from 0 to 1
laya-multilingual ships without fitted temperatures, so a displayed 0.90 is not a calibrated 90%. The injected text says the numbers are hints. Ordinal score is not used; Laya's own benchmarks show it is the weakest primitive.
Every call is appended to ~/.laya-router/data/routes.jsonl (timestamp, client, working directory, session, prompt unless disabled, scores, latency, question version). That file is the material for a later coding-specific fine-tune.
If the service is down, the hook prints {} and exits 0. Codex and Claude Code continue. The error is appended to ~/.laya-router/logs/hook.log.
Develop
PYTHONPATH=src uv run --no-project --with pytest python -m pytest
Unit tests do not load the model. After the service is up, laya-router route runs a real inference.
| File | Role | Picked up |
|---|---|---|
src/laya_router/questions.py |
Question text. Bump QUESTIONS_VERSION when the wording changes. |
laya-router restart |
src/laya_router/render.py |
Text injected into the agent | laya-router restart, then the next prompt |
src/laya_router/hook.py |
Timeout, slash-command skip, fail-open | laya-router restart, then the next prompt |
src/laya_router/server.py |
Model load, /route, dashboard |
laya-router restart |
src/laya_router/dashboard.html |
Browser UI | laya-router restart |
Do not run the server with --reload. A reloader starts extra processes and loads the model more than once.
Security
This repository does not contain API keys, account tokens, or model weights. The installer never uploads ~/.claude/settings.json or ~/.codex.
The service has no authentication. It listens on 127.0.0.1 only. /route accepts a prompt, and the dashboard shows stored prompts. Binding any other address makes both reachable by anyone who can open that port. The process prints a warning if host is not a loopback address. Do not put this port on a public interface, and do not reverse-proxy it without adding auth yourself.
routes.jsonl stores the raw prompt by default, including anything you paste into Codex or Claude Code. Set log_prompts = false to keep only a SHA-256. The runtime directory is ~/.laya-router/, outside this repo. Do not commit it.
The installer rewrites ~/.codex/hooks.json and merges a UserPromptSubmit entry into ~/.claude/settings.json. That Claude file often holds API credentials. The tool preserves existing keys, writes a timestamped *.bak-* copy before changing a file, and removes only its own hook on uninstall. Read the diff before you publish a dotfile.
A web page on another origin cannot read these responses: the server does not send Access-Control-Allow-Origin. Keep the default bind address anyway.
The hook adds a hint. It does not copy the user prompt back into the injected instructions.
Limits
The multilingual base checkpoint is a fast model to specialize, not a zero-shot coding router. Public typed-decision numbers for the base checkpoints sit near chance until a domain fine-tune. Expect some complex requests to be labeled simple. Use the log, then fine-tune, before trusting the scores to choose a model.
License
Apache License 2.0. The Laya model and library are also Apache-2.0.
