Document the local router for open source and warn when it is not bound to loopback.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
116
README.md
116
README.md
@@ -1,106 +1,140 @@
|
||||
# Laya Router Hook
|
||||
|
||||
本地常驻 [Laya](https://github.com/NandhaKishorM/laya) multilingual 模型,在 Codex 和 Claude Code 每次提交 prompt 时注入一条 System-1 路由提示。它不切换底层模型,只补充任务复杂度、是否需要计划、读仓库、查文档和深度推理的判断。
|
||||
[中文说明](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(标准库,失败则放行)
|
||||
-> UserPromptSubmit hook (stdlib only, fail-open)
|
||||
-> http://127.0.0.1:8765
|
||||
-> Laya multilingual
|
||||
-> additionalContext
|
||||
-> Codex / Claude Code
|
||||
```
|
||||
|
||||
Cursor 会加载 Claude Code 的 hook 配置,但当前事件无法把上下文注入对话。Hook 发现 `CURSOR_VERSION` 时直接返回,不增加延迟。
|
||||
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.
|
||||
|
||||
需要 macOS、[uv](https://docs.astral.sh/uv/getting-started/installation/) 和 Python 3.10+。
|
||||
## Install
|
||||
|
||||
macOS, [uv](https://docs.astral.sh/uv/getting-started/installation/), and Python 3.10+.
|
||||
|
||||
```bash
|
||||
./install.sh
|
||||
```
|
||||
|
||||
脚本会:
|
||||
The script:
|
||||
|
||||
1. 在 `~/.laya-router/.venv` 创建虚拟环境,并把本仓库安装进去
|
||||
2. 下载 `convaiinnovations/laya` 的 `multilingual` 权重
|
||||
3. 注册 launchd 用户服务 `com.laya-router`(登录自启,进程退出后拉起)
|
||||
4. 检测到 Codex / Claude Code 时写入 hook
|
||||
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
|
||||
|
||||
安装的是一份普通副本,不是指向仓库的可编辑安装。仓库如果在 `~/Documents` 下,launchd 直接 import 那个目录会卡在文件打开上。`laya-router restart` 会先把当前仓库同步进虚拟环境再重启。
|
||||
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
|
||||
```
|
||||
|
||||
Codex 第一次会要求信任 hook。启动 `codex` 后运行 `/hooks` 确认。命令行本身不变时,修改 Python 不会再次触发信任审查。
|
||||
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
|
||||
|
||||
命令来自 `~/.laya-router/.venv`。`./install.sh` 之后可以把这个 `bin` 加到 `PATH`,或直接用绝对路径。
|
||||
|
||||
浏览器打开 [http://127.0.0.1:8765/](http://127.0.0.1:8765/) 可以按会话看每次判断。页面通过服务端事件实时追加,左边是会话,右边是这一路 prompt 的判定过程。
|
||||
After install, the binaries live in `~/.laya-router/.venv/bin`.
|
||||
|
||||
```bash
|
||||
laya-router status
|
||||
laya-router logs
|
||||
laya-router logs -f
|
||||
laya-router route "帮我把这个函数的变量名改掉"
|
||||
laya-router route "Rename the variables in this function"
|
||||
laya-router restart
|
||||
laya-router stop
|
||||
laya-router start
|
||||
```
|
||||
|
||||
卸载 hook 和服务,但保留运行目录:
|
||||
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` 在首次安装时生成,之后不会被覆盖。
|
||||
`~/.laya-router/config.toml` is written on first install and is not overwritten later.
|
||||
|
||||
| 键 | 含义 |
|
||||
| Key | Meaning |
|
||||
| --- | --- |
|
||||
| `device` | `auto`、`mps`、`cuda` 或 `cpu`。`auto` 预热失败会回退 |
|
||||
| `log_prompts` | 是否把原文写入 `data/routes.jsonl` |
|
||||
| `request_timeout` | Hook 等待服务的秒数,默认 2.5 |
|
||||
| `max_prompt_chars` | 送入模型的最大字符数,超长保留头尾 |
|
||||
| `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. |
|
||||
|
||||
改完配置或服务端代码后执行 `laya-router restart`。
|
||||
Restart after config or server changes: `laya-router restart`.
|
||||
|
||||
## 迭代
|
||||
## How a decision is made
|
||||
|
||||
改完保存后执行 `laya-router restart`。它会把仓库同步进 `~/.laya-router` 再拉起服务。Hook 是每次 prompt 新起的进程,同步完成后下一条 prompt 就会用到新的 `hook.py` 和 `render.py`。
|
||||
One forward pass asks five questions:
|
||||
|
||||
| 文件 | 作用 | 生效方式 |
|
||||
| --- | --- | --- |
|
||||
| `src/laya_router/questions.py` | 路由问题。改措辞时把 `QUESTIONS_VERSION` 加一 | `laya-router restart` |
|
||||
| `src/laya_router/render.py` | 注入给 Agent 的提示文本 | `laya-router restart`,然后下一条 prompt |
|
||||
| `src/laya_router/hook.py` | 超时、跳过斜杠命令、失败放行 | `laya-router restart`,然后下一条 prompt |
|
||||
| `src/laya_router/server.py` | 模型加载与 `/route` | `laya-router restart` |
|
||||
- `task_level`: `simple`, `normal`, or `complex`
|
||||
- `needs_plan`, `needs_repo`, `needs_web`, `needs_deep_reasoning`: probabilities from 0 to 1
|
||||
|
||||
不要给服务加 `--reload`。开发模式会再起进程,把模型加载多次。
|
||||
`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.
|
||||
|
||||
路由记录在 `~/.laya-router/data/routes.jsonl`,包含判断、耗时和问题版本,用来以后对照真实任务做微调。`laya-multilingual` 没有自带温度校准,注入文本里的概率只是提示。
|
||||
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.
|
||||
|
||||
服务挂掉时 hook 输出 `{}` 并退出 0,Codex 和 Claude Code 照常工作。原因写在 `~/.laya-router/logs/hook.log`。
|
||||
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
|
||||
```
|
||||
|
||||
单元测试不加载模型。装好服务后用 `laya-router route` 做真实推理检查。
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user