141 lines
6.3 KiB
Markdown
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.
|