From 10cc9c90c014b221c86e7802b36cfdd12010703b Mon Sep 17 00:00:00 2001
From: Evandro Fonseca Junior <1+evandro@noreply.git.evandro.dev.br>
Date: Sat, 24 Oct 2026 04:13:03 +0000
Subject: [PATCH] docs(readme): padroniza README (hero, badges, quickstart,
arquitetura)
---
README.md | 263 +++++++++++++++++++++++++++++++-----------------------
1 file changed, 150 insertions(+), 113 deletions(-)
diff --git a/README.md b/README.md
index 732c126..702a856 100644
--- a/README.md
+++ b/README.md
@@ -1,162 +1,199 @@
-# ForgeLM
+
ForgeLM
-**Inferência local de LLMs `.gguf` em Rust: API HTTP no estilo OpenAI, CLI e app
-desktop Tauri com chat persistente e gerenciador de modelos — tudo rodando na sua máquina.**
+
Inferência local de LLMs .gguf em Rust: API no estilo OpenAI, CLI e app desktop Tauri, tudo na sua máquina.
-
-
-
+
Quickstart ·
+
Arquitetura ·
+
Documentação ·
+
Issues
-[Início rápido](#in%C3%ADcio-r%C3%A1pido) · [Recursos](#o-que-existe-hoje) · [Como funciona](#como-funciona) · [Limitações](#limita%C3%A7%C3%B5es-conhecidas) · [Documentação](#documenta%C3%A7%C3%A3o)
+
+
+
+
+
> [!NOTE]
-> **Estado do projeto:** em desenvolvimento (último commit em abril/2026). Segundo
-> [`.planning/ROADMAP.md`](.planning/ROADMAP.md), as fases 1–4 (estado de rascunho do
-> runtime, segurança dos comandos de ciclo de vida, workspace unificado e download de
-> modelos do Hugging Face) estão concluídas e a fase 5 (validação de fontes de modelo)
-> segue aberta. O histórico também registra os planos 06-01 a 06-03 (redesign do chat
-> e da página de Configurações).
+> **Este projeto não está concluído.** O que está descrito abaixo é o que existe hoje no código. Segundo [`.planning/ROADMAP.md`](.planning/ROADMAP.md), as fases 1–4 estão concluídas; a fase 5 (validação de fontes de modelo) segue aberta no roadmap e a fase 6 (redesign do chat) consta como planejada.
-> [!IMPORTANT]
-> **A API sobe aberta por padrão.** Sem `LOCALLLM_API_KEY` não há autenticação, o
-> bind default é `0.0.0.0:8080` e o CORS libera `*`. Para uso fora de uma máquina
-> confiável, defina a chave e restrinja `LOCALLLM_ADDR` e `LOCALLLM_CORS_ORIGINS`
-> (veja [`docs/api/configuration.md`](docs/api/configuration.md)).
+## 📌 O problema
-## O que existe hoje
+Rodar um modelo `.gguf` na própria máquina exige juntar peças soltas: um runtime do llama.cpp compatível com a sua GPU, uma API que clientes no estilo OpenAI consigam chamar e um lugar para escolher modelos e conversar. O ForgeLM reúne essas peças em um workspace Rust local-first, sem depender de infraestrutura hospedada, e documenta o que ainda não funciona.
-| Superfície | Onde | O que entrega |
-|---|---|---|
-| **API HTTP local** | [`crates/inference-server`](crates/inference-server) | `GET /health`, `GET /v1/models` e `POST /v1/chat/completions` (com streaming SSE e `stream_options.include_usage`) |
-| **CLI `localllm`** | [`crates/localllm-cli`](crates/localllm-cli) | Comandos `status`, `models`, `auth`, `config`, `bench`, `logs` e `doctor`; flags globais `--server`, `--api-key`, `--models-dir`, `--format text\|json` e `--dry-run` |
-| **App desktop** | [`tauri-app`](tauri-app) | Runtime manager (modo `local` ou `remote`), chat persistente e página de Configurações com gerenciador de modelos |
-| **Perfis de runtime** | [`crates/inference-core`](crates/inference-core) | `Turbo4B` (default), `balanced_9b` e `cpu_fallback`, ajustáveis por `LOCALLLM_GPU_LAYERS`, `LOCALLLM_CTX`, `LOCALLLM_BATCH`, etc. |
-| **Runtime llama.cpp gerenciado** | [`crates/runtime-deps`](crates/runtime-deps) | Baixa o llama.cpp `b8358` (variantes CPU e CUDA 12.4), valida SHA-256 e guarda em cache local |
+## ✨ O que faz (o que existe hoje)
-No app desktop:
+- **Serve** `GET /health`, `GET /v1/models` e `POST /v1/chat/completions` em `:8080`, com streaming SSE e `stream_options.include_usage`.
+- **Carrega** modelos `.gguf` via llama.cpp (carga dinâmica) com os perfis `Turbo4B` (default), `balanced_9b` e `cpu_fallback`, ajustáveis por variáveis de ambiente.
+- **Baixa e valida** o runtime llama.cpp `b8358` (CPU ou CUDA 12.4, com SHA-256) quando ele não é encontrado.
+- **Opera** pela CLI `localllm`: `status`, `models`, `auth`, `config`, `bench`, `logs` e `doctor`.
+- **Gerencia** o runtime no app desktop (modo `local` ou `remote`; estados `stopped`, `starting`, `ready`, `degraded`, `stopping` e `error`).
+- **Escaneia, importa, busca e baixa** modelos do Hugging Face com progresso e cancelamento (token opcional) e troca o modelo carregado com um clique (*hot-swap*).
+- **Conversa** com streaming, memória opcional de até 20 mensagens, separação de `…`, contagem de tokens, tokens/s e edição, reenvio e exclusão de mensagens.
+- **Protege** a API com chave opcional (`LOCALLLM_API_KEY`), CORS configurável e aliases de modelo.
-- **Runtime manager** com estados `stopped`, `starting`, `ready`, `degraded`, `stopping` e `error`; o modo local inicia/para o processo e usa `GET /health` para saber se está pronto.
-- **Gerenciador de modelos:** escaneia/importa `.gguf`, busca modelos no Hugging Face, baixa em streaming com progresso e cancelamento (token do Hugging Face opcional), troca o modelo carregado com um clique (*hot-swap*: para, grava o novo `model_path` e reinicia), apaga arquivos e abre a pasta no explorador.
-- **Chat:** streaming, memória opcional de até 20 mensagens recentes, separação visual do raciocínio (`…`) da resposta final, contagem de tokens (por mensagem e por chat) e tokens/s, edição, reenvio e exclusão de mensagens.
+## 🖼️ Demonstração
-## Início rápido
+Capturas do shell do app com a API local offline e nenhum modelo selecionado: mostram o layout, não uma inferência em andamento.
+
+
+
+
+
+
+
+
+
+
+## 🚀 Quickstart
> [!WARNING]
-> **Na primeira execução o servidor baixa o runtime do llama.cpp.** Se o runtime não
-> for encontrado (`LLAMA_DLL_PATH`, ao lado do binário ou vendor legado), o `runtime-deps` baixa o
-> pacote de `github.com/ggml-org/llama.cpp` (`b8358`). A variante CUDA 12.4 são dois
-> zips (≈ 227 MB + ≈ 391 MB conforme o manifesto); a CPU, um zip de ≈ 37 MB. O
-> servidor escolhe CUDA quando as camadas em GPU são `> 0` e cai para CPU caso não
-> exista variante de GPU para o alvo.
+> **A API sobe aberta por padrão.** Sem `LOCALLLM_API_KEY` não há autenticação, o bind default é `0.0.0.0:8080` e o CORS libera `*`. Fora de uma máquina confiável, defina a chave e restrinja `LOCALLLM_ADDR` e `LOCALLLM_CORS_ORIGINS`.
-**1. Subir a API local** (Windows `cmd`; em bash use `export`):
+| Ferramenta | Versão mínima |
+|---|---|
+| Rust (cargo) | edition 2021; `rust-version` não declarada |
+| Node.js + npm (só para o app) | não declarada (sem `.nvmrc` nem `engines`) |
+| Modelo `.gguf` local | obrigatório |
+| GPU NVIDIA | opcional (variante CUDA 12.4; existe o perfil `cpu_fallback`) |
+
+**Na primeira execução o servidor baixa o llama.cpp `b8358`** de `github.com/ggml-org/llama.cpp` se não achar o runtime (`LLAMA_DLL_PATH`, binário adjacente ou vendor legado). CUDA 12.4 são dois zips (≈ 227 MB + ≈ 391 MB); CPU, um zip de ≈ 37 MB. O manifesto só lista `windows-x64`.
```bash
-set LOCALLLM_MODEL=D:\caminho\modelo.gguf
+# 1. API local (Windows cmd; em bash use `export`)
+set LOCALLLM_MODEL=D:\modelos\meu-modelo.gguf
cargo run -p inference-server
curl http://127.0.0.1:8080/health
curl http://127.0.0.1:8080/v1/models
-```
+curl -N http://127.0.0.1:8080/v1/chat/completions \
+ -H "Content-Type: application/json" \
+ -d '{"model":"default","messages":[{"role":"user","content":"Diga ola"}],"stream":true}'
-**2. Usar a CLI:**
-
-```bash
+# 2. CLI
cargo run -p localllm-cli -- status
cargo run -p localllm-cli -- models list
-```
-**3. Abrir o app desktop:**
-
-```bash
+# 3. App desktop
cd tauri-app
npm install
npm run tauri dev
```
-No app, abra o Runtime Manager, escolha modo `local` ou `remote` e depois use o chat.
+### Variáveis de ambiente
-**Requisitos:** toolchain Rust atual, Node.js/NPM (app Tauri) e um modelo `.gguf` local.
-GPU NVIDIA é opcional — existe o perfil `cpu_fallback`.
+Extraídas do código e de [`docs/api/configuration.md`](docs/api/configuration.md); não há `.env.example`. O prefixo é `LOCALLLM_` (três L).
-## Como funciona
+| Variável | Obrigatória | Descrição |
+|---|---|---|
+| `LOCALLLM_MODEL` | Sim | Caminho do `.gguf`; sem ele o engine aborta (`model_path is empty`) |
+| `LOCALLLM_ADDR` | Não | Endereço de bind; default `0.0.0.0:8080` |
+| `LOCALLLM_MODEL_ALIASES` | Não | Aliases do modelo, separados por vírgula, expostos em `GET /v1/models` |
+| `LOCALLLM_PROFILE` | Não | `balanced_9b` ou `cpu_fallback`; ausente ou outro valor = `Turbo4B` |
+| `LOCALLLM_GPU_LAYERS` | Não | Camadas na GPU; default do perfil (999 em `Turbo4B`/`balanced_9b`, 0 em `cpu_fallback`) |
+| `LOCALLLM_CTX` | Não | Janela de contexto; default do perfil (4096, 2048 e 2048) |
+| `LOCALLLM_BATCH` | Não | `n_batch`; sem valor explícito, usa o do perfil limitado a 4096 |
+| `LOCALLLM_UBATCH` | Não | `n_ubatch`; default do perfil |
+| `LOCALLLM_THREADS` | Não | Threads de geração; default do perfil |
+| `LOCALLLM_THREADS_BATCH` | Não | Threads de prefill; default do perfil |
+| `LOCALLLM_API_KEY` | Não | Habilita autenticação por chave; vazia ou ausente = API aberta |
+| `LOCALLLM_AUTH_HEADER` | Não | Cabeçalho preferencial da chave; default `authorization` (também aceita `x-api-key`) |
+| `LOCALLLM_AUTH_SCHEME` | Não | Prefixo removido antes da comparação (ex.: `Bearer`) |
+| `LOCALLLM_CORS_ORIGINS` | Não | Allowlist de origens separadas por vírgula; default `*` |
+| `LOCALLLM_SYSTEM_PROMPT` | Não | Prompt de sistema usado quando a request não traz `system` nem `developer` |
+| `LOCALLLM_ENABLE_TOOL_CALLS` | Não | Liga/desliga tool calling; default ligado |
+| `LOCALLLM_LOG_FORMAT` | Não | `json` para logs estruturados; default `pretty` |
+| `LLAMA_DLL_PATH` | Não | Caminho completo do `llama.dll`; sobrepõe a resolução automática do runtime |
+| `RUST_LOG` | Não | Filtro de logs do `tracing`; default `info` |
+
+A pasta de modelos **não** é variável de ambiente: na CLI é a flag global `--models-dir` (default `./models`); no app é o campo `models_dir` da configuração do runtime. Ao iniciar o servidor local, o app define `LOCALLLM_MODEL`, `LOCALLLM_ADDR`, `LOCALLLM_GPU_LAYERS` e `LOCALLLM_CTX`.
+
+## 🧱 Arquitetura
```mermaid
flowchart LR
- UI["App Tauri
React + Rust"] -->|"HTTP /v1/chat/completions"| S
- UI -->|"inicia / para / health"| S["inference-server
Axum · binário localllm"]
- CLI["localllm-cli"] -->|HTTP| S
- S --> C["inference-core
engine · sessão · sampler · perfis"]
- C --> L["llama-sys
carga dinâmica (libloading)"]
- L --> R["llama.dll
llama.cpp b8358"]
- D["runtime-deps
download + SHA-256"] --> R
- S --> D
- UI -->|"busca e download"| HF["Hugging Face"]
- S -.-> M["metrics
tokens e throughput"]
+ UI["App Tauri (React + Rust)"] -->|"HTTP e SSE"| SRV["inference-server (Axum)"]
+ CLI["localllm-cli"] -->|"HTTP"| SRV
+ SRV --> CORE["inference-core (engine, sessão, perfis)"]
+ CORE --> SYS["llama-sys (llama.dll via libloading)"]
+ SRV --> DEPS["runtime-deps (download + SHA-256)"]
+ DEPS -->|"llama.cpp b8358"| SYS
+ UI -->|"busca e download de .gguf"| HF["Hugging Face"]
```
-
-Crates e módulos
+- O app (no modo `local`) inicia e para o `inference-server` e usa `GET /health` para saber se está pronto; no modo `remote` apenas fala com a API.
+- A CLI é um cliente HTTP da mesma API.
+- O `inference-server` valida a request (auth, CORS, aliases) e delega ao `inference-core`, que mantém o engine, a sessão e o sampler.
+- O `inference-core` usa o `llama-sys`, que carrega `llama.dll` dinamicamente; o `runtime-deps` resolve ou baixa esse runtime a partir de [`vendor/llama-runtime/manifest.json`](vendor/llama-runtime/manifest.json).
+- O app busca e baixa modelos direto do Hugging Face, sem passar pela API local.
-| Módulo | Responsabilidade |
+## 🗂️ Estrutura
+
+```text
+crates/
+ api-contracts/ tipos compartilhados (chat, modelos, health, erros)
+ inference-core/ engine de inferência, sessão, sampler e perfis
+ inference-server/ API HTTP em Axum (binário localllm)
+ llama-sys/ carga dinâmica do llama.cpp (libloading)
+ metrics/ contadores de tokens e throughput
+ runtime-deps/ resolve e baixa o runtime llama.cpp
+ localllm-cli/ CLI (binário localllm)
+tauri-app/ app desktop: React 18 + Vite + Tailwind, backend Tauri 2
+ui/ páginas HTML estáticas de desenvolvimento (index.html, chat.html)
+docs/ documentação detalhada
+```
+
+## 🧪 Scripts
+
+| Comando | O que faz |
|---|---|
-| `crates/api-contracts` | Tipos compartilhados: chat completions, lista de modelos, health e erros |
-| `crates/inference-core` | Engine de inferência local: carga do modelo, sessão, sampler e perfis de runtime |
-| `crates/inference-server` | Camada HTTP em Axum: variáveis de ambiente, auth, CORS e aliases de modelo |
-| `crates/localllm-cli` | CLI de operação e automação (clap) |
-| `crates/metrics` | Contadores de uso de tokens e throughput |
-| `crates/llama-sys` | Integração de baixo nível: carrega `llama.dll` dinamicamente via `libloading` |
-| `crates/runtime-deps` | Resolve o runtime (override por env, binário adjacente, vendor legado ou cache gerenciado) e instala a partir de [`vendor/llama-runtime/manifest.json`](vendor/llama-runtime/manifest.json) |
-| `tauri-app/` | App desktop: frontend React 18 + Vite + Tailwind, backend Tauri 2 em Rust |
+| `cargo run -p inference-server` | Sobe a API em `0.0.0.0:8080` (exige `LOCALLLM_MODEL`) |
+| `cargo run -p localllm-cli -- ` | Executa a CLI |
+| `cargo test` | Testes dos crates do workspace |
+| `cargo test --manifest-path tauri-app/src-tauri/Cargo.toml` | Testes do backend Tauri (fora do workspace) |
+| `npm run dev` (em `tauri-app`) | Só o frontend, Vite na porta 1420 |
+| `npm run tauri dev` | App desktop completo |
+| `npm run build` | `tsc && vite build` |
+| `npm run preview` | `vite preview` |
+| `npm test` | `vitest run` |
+| `npm run tauri` | Repassa argumentos ao `tauri` CLI |
+| `scripts/build-dev.sh` (há `.ps1`) | Compila `vendor/llama.cpp` em Debug com CUDA |
+| `scripts/build-prod.sh` (há `.ps1`) | Compila `vendor/llama.cpp` em Release com LTO e CUDA (arch 86) |
-Variáveis de ambiente do servidor (principais): `LOCALLLM_MODEL`, `LOCALLLM_ADDR`,
-`LOCALLLM_MODEL_ALIASES`, `LOCALLLM_PROFILE`, `LOCALLLM_API_KEY`,
-`LOCALLLM_AUTH_HEADER`, `LOCALLLM_AUTH_SCHEME`, `LOCALLLM_CORS_ORIGINS`,
-`LOCALLLM_SYSTEM_PROMPT` e `LOCALLLM_ENABLE_TOOL_CALLS`. Detalhes em
-[`docs/api/configuration.md`](docs/api/configuration.md).
-
-
-
-## Limitações conhecidas
-
-- A API expõe só três endpoints; a compatibilidade com a OpenAI é parcial e focada no fluxo de chat (`n` precisa ser `1`, `parallel_tool_calls` não é tratado como recurso suportado, `content` em partes vira texto consolidado).
-- O raciocínio não aparece como campo separado na API; quem separa `…` é o frontend do app.
-- Na CLI, a execução ao vivo está mais consolidada em `status`, `models list` e `bench throughput`; o restante da superfície é mais avançado no parser do que nas capacidades HTTP públicas.
-- O chat usa `sql.js` carregado por CDN e persiste o banco exportado em `localStorage` — não é um banco nativo do sistema.
-- No modo local o app precisa encontrar o binário `localllm`; o runtime manager faz polling periódico do snapshot (sem fluxo de eventos push).
-- O manifesto do runtime ([`vendor/llama-runtime/manifest.json`](vendor/llama-runtime/manifest.json)) só lista variantes `windows-x64` (CPU e CUDA 12.4). Em outros alvos o servidor só sobe com um runtime já presente (binário adjacente ou `LLAMA_DLL_PATH`).
-- Os scripts [`scripts/build-dev.sh`](scripts/build-dev.sh) e [`scripts/build-prod.sh`](scripts/build-prod.sh) compilam `vendor/llama.cpp` e exigem esse submódulo, que não está declarado no repositório (não há `.gitmodules`).
-- `inference-server` e `localllm-cli` declaram ambos um binário chamado `localllm`; use `cargo run -p ` para escolher qual executar.
-
-## Execução local
-
-```bash
-cargo run -p inference-server # API em 0.0.0.0:8080 (precisa de LOCALLLM_MODEL)
-cargo run -p localllm-cli -- status # CLI
-
-cd tauri-app
-npm install
-npm run dev # só o frontend (Vite, porta 1420)
-npm run tauri dev # app desktop completo
-npm run build # tsc + vite build
-npm test # vitest run
-```
-
-## Documentação
+## 📚 Documentação
| Documento | Conteúdo |
|---|---|
| [`docs/README.md`](docs/README.md) | Índice da documentação |
-| [`docs/api/overview.md`](docs/api/overview.md) · [`endpoints.md`](docs/api/endpoints.md) · [`configuration.md`](docs/api/configuration.md) | API: visão geral, referência HTTP e configuração |
-| [`docs/cli/overview.md`](docs/cli/overview.md) · [`commands.md`](docs/cli/commands.md) | CLI: visão geral e referência de comandos |
-| [`docs/app/overview.md`](docs/app/overview.md) · [`user-guide.md`](docs/app/user-guide.md) · [`backend-commands.md`](docs/app/backend-commands.md) | App desktop: visão geral, guia de uso e comandos Tauri |
-| [`docs/developer/architecture.md`](docs/developer/architecture.md) | Arquitetura do workspace e fluxos entre módulos |
+| [`docs/api/overview.md`](docs/api/overview.md) · [`endpoints.md`](docs/api/endpoints.md) · [`configuration.md`](docs/api/configuration.md) | API: visão geral, endpoints e configuração |
+| [`docs/cli/overview.md`](docs/cli/overview.md) · [`commands.md`](docs/cli/commands.md) | CLI: visão geral e comandos |
+| [`docs/app/overview.md`](docs/app/overview.md) · [`user-guide.md`](docs/app/user-guide.md) · [`backend-commands.md`](docs/app/backend-commands.md) | App: visão geral, guia de uso e comandos Tauri |
+| [`docs/developer/architecture.md`](docs/developer/architecture.md) | Mapa do workspace e fluxos entre módulos |
+| [`.planning/ROADMAP.md`](.planning/ROADMAP.md) | Fases e estado do desenvolvimento |
-## Avisos
+## 🛣️ Limitações e próximos passos
-- Os binários de inferência vêm do [llama.cpp](https://github.com/ggml-org/llama.cpp) (releases oficiais baixadas e verificadas por SHA-256); modelos são buscados no [Hugging Face](https://huggingface.co) e seguem a licença de cada autor.
-- Licença do workspace: MIT (declarada em [`Cargo.toml`](Cargo.toml); o repositório não inclui arquivo `LICENSE`).
+- A API expõe só três endpoints; a compatibilidade com a OpenAI é parcial: `n` precisa ser `1`, `parallel_tool_calls` não é recurso suportado e `content` em partes vira texto consolidado.
+- O raciocínio não é um campo separado na API; quem separa `…` é o frontend do app.
+- Na CLI, a execução ao vivo está mais consolidada em `status`, `models list` e `bench throughput`; o resto da superfície está mais avançado no parser do que nas capacidades HTTP públicas.
+- O chat usa `sql.js` carregado por CDN e persiste o banco exportado em `localStorage`; não é um banco nativo do sistema.
+- No modo local o app precisa encontrar o binário `localllm`, e o runtime manager faz polling do snapshot (sem eventos push).
+- O manifesto do runtime só lista variantes `windows-x64`; em outros alvos o servidor só sobe com um runtime já presente (binário adjacente ou `LLAMA_DLL_PATH`).
+- `scripts/build-dev.sh` e `scripts/build-prod.sh` exigem o submódulo `vendor/llama.cpp`, que não está declarado no repositório (não há `.gitmodules`).
+- `inference-server` e `localllm-cli` declaram ambos um binário chamado `localllm`; use `cargo run -p ` para escolher.
+- Próximos passos registrados no roadmap: concluir a fase 5 (validação de fontes de modelo, com seus três planos marcados como feitos mas a fase ainda aberta) e a fase 6 (redesign do chat e da página de Configurações, 0/3 planos).
+
+## 🤝 Contribuindo
+
+Abra uma issue ou PR descrevendo a mudança. Antes de enviar, rode os testes que cobrem o que você alterou:
+
+```bash
+cargo test && (cd tauri-app && npm test)
+```
+
+## 📄 Licença
+
+MIT, declarada em [`Cargo.toml`](Cargo.toml); o repositório não inclui arquivo `LICENSE`. Os binários de inferência vêm do [llama.cpp](https://github.com/ggml-org/llama.cpp) e os modelos do [Hugging Face](https://huggingface.co) seguem a licença de cada autor.