focus/README.md

125 lines
3.7 KiB
Markdown

# focus
An LLM agent framework in Rust, structured as a Cargo workspace. Built to learn
the architecture of [pi](https://github.com/earendil-works/pi) by reimplementing
its core layers with a minimal dependency surface (tokio + rustls only).
This milestone focuses on calling **Z.ai / 智谱 GLM coding models**
(`glm-5.2`, domestic endpoint) end-to-end.
## Quick start
### 1. Create a config file
Copy the example (it lists providers but **no API keys** — those go in
`auth.json` via `/login`):
```bash
mkdir -p ~/.focus
cp examples/config.example.json ~/.focus/config.json
```
The config (`~/.focus/config.json`) selects a default provider and lists
available ones (host, model). Override the config dir with `FOCUS_CONFIG_DIR`.
### 2. Log in (save your API key)
Run `focus` with no arguments to enter the interactive REPL, then use the
`/login` slash command:
```bash
./target/debug/focus
> /login zai-coding-cn
Enter API key for zai-coding-cn: <paste your 智谱 API key>
Credentials saved to ~/.focus/auth.json
> /quit
```
Your key is stored in `~/.focus/auth.json` (mode `0600`). Log out with
`/logout [provider]`.
### 3. Run
```bash
cargo build --workspace
# Interactive REPL (multi-turn conversation):
./target/debug/focus
> 你好
你好!有什么可以帮你的?
> /quit
# Single prompt (one-shot):
./target/debug/focus "用一句话解释什么是 Rust"
# Or pipe via stdin:
echo "写一个快排" | ./target/debug/focus
# Override the model:
./target/debug/focus --model glm-5.2 "hello"
# One-off API key (in-memory only, not saved):
./target/debug/focus --api-key YOUR_KEY "hello"
# Print the resolved config:
./target/debug/focus --config
```
`focus --help` shows all options.
### REPL slash commands
Inside the REPL (`focus` with no prompt, on a TTY):
| Command | Action |
|---|---|
| `/login [provider]` | store an API key in `~/.focus/auth.json` |
| `/logout [provider]` | remove a stored credential |
| `/provider [name]` | show or switch the active provider |
| `/model [name]` | show or switch the active model |
| `/system [text]` | show or set the system prompt |
| `/clear` / `/new` | clear the conversation transcript |
| `/config` | print the resolved provider config |
| `/help` | list commands |
| `/quit` | exit focus |
Plain text is sent to the model as a prompt; the conversation is multi-turn
(the transcript persists across prompts).
### Credential resolution order
When running a prompt, the API key is resolved in this order (highest first):
1. `--api-key` CLI flag (in-memory only, never persisted)
2. `~/.focus/auth.json` entry (written by `focus login`)
3. `config.json` provider `apiKey` field (if present)
### 3. Run the real-call smoke test (optional)
```bash
ZAI_API_KEY=<key> cargo test -p focus-providers --test zai_smoke -- --ignored --nocapture
```
## Architecture
See [`docs/architecture/`](./docs/architecture/) for the pi reference analysis.
The workspace mirrors pi's layering:
```
focus-json minimal JSON parser/serializer (zero deps)
focus-core domain types, agent loop, Tool/StreamProvider traits
focus-transport HTTP/1.1, TLS (rustls), SSE parser
focus-providers Z.ai / OpenAI-compat + Anthropic providers
focus-cli config loading + single-prompt runner
focus-tools (placeholder) file/shell tools
focus-harness (placeholder) session persistence + compaction
```
Dependency flow is strictly one-way: `core` never depends on transport,
providers, or tools. All I/O crosses trait boundaries.
## Verification
```bash
cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
```
All 95 unit/integration tests pass with zero clippy warnings. See
[`AGENTS.md`](./AGENTS.md) for the full engineering spec.