125 lines
3.7 KiB
Markdown
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.
|