Go to file
DaiChaoXiong 8ed56c3952 feat(providers): rewrite Anthropic and OpenAI providers
- Anthropic Messages API (2023-06-01) SSE streaming: text/thinking/tool_use
  blocks, cache usage, stop-reason mapping, errors encoded as events
- OpenAI Responses API + Chat Completions (switchable), tool_calls delta
  accumulation, include_usage, reasoning_content -> thinking
- ProviderConfig with base_url/api_key/context_window/timeout; known-model
  context-window table with three-layer resolution
- background current-thread runtime bridges sync agent loop to async I/O
- mock-transport replay tests (chunks straddle SSE boundaries) + full agent
  integration test
2026-08-09 20:59:49 +08:00
crates feat(providers): rewrite Anthropic and OpenAI providers 2026-08-09 20:59:49 +08:00
docs/architecture INIT 2026-07-16 17:59:16 +08:00
examples INIT 2026-07-16 17:59:16 +08:00
.gitignore INIT 2026-07-16 17:59:16 +08:00
AGENTS.md ADD: 注释 2026-07-17 11:19:22 +08:00
Cargo.lock INIT 2026-07-16 17:59:16 +08:00
Cargo.toml INIT 2026-07-16 17:59:16 +08:00
README.md INIT 2026-07-16 17:59:16 +08:00
rust-toolchain.toml INIT 2026-07-16 17:59:16 +08:00

README.md

focus

An LLM agent framework in Rust, structured as a Cargo workspace. Built to learn the architecture of 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):

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:

./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

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)

ZAI_API_KEY=<key> cargo test -p focus-providers --test zai_smoke -- --ignored --nocapture

Architecture

See 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

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 for the full engineering spec.