Files
laya-hook/README.md

141 lines
6.3 KiB
Markdown

# Laya Router Hook
[中文说明](README.zh-CN.md)
A local [Laya](https://github.com/NandhaKishorM/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.
```text
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/](http://127.0.0.1:8765/) shows each decision, grouped by session, and updates over server-sent events.
Cursor loads Claude Code hooks, but its prompt event cannot inject context. The hook sees `CURSOR_VERSION` and returns immediately.
## Install
macOS, [uv](https://docs.astral.sh/uv/getting-started/installation/), and Python 3.10+.
```bash
./install.sh
```
The script:
1. Creates `~/.laya-router/.venv` and installs this repo into it
2. Downloads the `multilingual` weights of `convaiinnovations/laya`
3. Registers a launchd agent, `com.laya-router`, that starts at login and restarts if it exits
4. 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:
```bash
./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`.
```bash
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:
```bash
./uninstall.sh
```
Also delete config, logs, and the virtualenv:
```bash
./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`, or `complex`
- `needs_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
```bash
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
No license file is in this repository yet. Add one before you publish it. The Laya model and library are Apache-2.0; this project's license is a separate choice.