diff --git a/.gitignore b/.gitignore index 0a0228b..4a2d623 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,26 @@ build/ *.log transcripts/ outputs/ +config/machine.toml +docs/downstream/ +docs/**/artifacts/ + +# Local telemetry / runtime artifacts +telemetry/ +**/telemetry/ +models/**/config/logs/ +*.jsonl +*session*.json +*session*.jsonl + +# Machine-local config files +config.local.toml +config.local.json +*.local.toml +*.local.json +machine.toml +machine.json +models/**/config/config.json # Model files (keep weights out of git if they ever land here) *.safetensors @@ -43,10 +63,17 @@ models/** !models/**/notes/** !models/**/config/** !models/**/templates/** +# Re-ignore machine/runtime artifacts inside otherwise allowed config trees +models/**/config/config.json +models/**/config/logs/** +models/**/config/telemetry/** +models/**/config/**/*.jsonl +models/**/config/**/*session*.json +models/**/config/**/*session*.jsonl # Never commit prompts (may contain secrets) models/**/prompts/** !models/**/prompts/README.md alex.jinja -*character/ \ No newline at end of file +*character/ diff --git a/AGENTS.md b/AGENTS.md index 4548ee2..9029921 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,31 +1,136 @@ # AGENTS ## Purpose -This repository hosts lightweight local model runners for multiple backends: -- Hugging Face causal language models (`transformers` + `torch`) -- GGUF models (`llama-cpp-python`) -- Ollama API models (streaming wrapper) +This repo is a local model lab for: +- local model research +- runtime tuning and backend comparison +- agent and application experimentation +- model-specific notes/config/template management + +It spans both: +- inference/runtime engineering +- AI application and agent development testing + +Treat it as a practical research workspace, not just a terminal chat app. + +Primary goals: +- run lots of local models consistently across multiple backends +- make runtime behavior visible enough to learn from and compare +- keep model-specific knowledge in-repo next to the configs/templates that matter +- make backend experimentation fast, local, and repeatable + +## Priority Order +When making engineering tradeoffs, prefer: +1. runtime truth and observability +2. comparable behavior across backends +3. local-first experimentation speed +4. enjoyable interaction surfaces + +If a UX summary conflicts with backend truth, fix the summary instead of hiding the truth. ## Scope -- Keep the project small and practical. -- Prioritize simple CLI flows for loading models and chatting. -- Prefer local-first execution with PyTorch + Transformers. +- Keep the project practical and easy to navigate. +- Favor local-first execution; avoid unnecessary services. +- Preserve backend diversity instead of forcing one engine to define the whole repo. +- Prefer standardized reporting where possible, but do not flatten away backend-specific facts that matter for tuning. +- Notes-first workflow: document behavior before changing code when possible. + +## Repo Operating Model +This repo uses two complementary document types: + +- **Decision records** + - capture contracts, invariants, and repo-wide rules discovered or established over time + - define what future work must preserve unless the decision itself is intentionally revised + - should be treated as contributor-facing truth, not as optional historical commentary + - live under `docs/decisions/` + +- **Specs** + - describe a concrete implementation pass + - turn a direction into executable engineering work + - should follow existing decision records instead of silently redefining them + - live under `docs/specs/` + +Contributor rule of thumb: +- check decision records when you need to know what must remain true +- check specs when you need to know what to build next +- if a coding pass reveals a new invariant or repo-wide contract, write it down as a decision instead of leaving it implicit in the codebase + +Do not treat chat history as the source of truth when a decision or spec should exist in-repo. + +## Shallow Map +Use this as the repo’s first-pass navigation graph. Start narrow and only drill deeper when the task requires it. + +- `README.md` + - front door: repo identity, current state, direction, major entry points +- `docs/decisions/` + - contracts and invariants that future work is expected to preserve +- `docs/specs/` + - implementation passes and scoped engineering instructions +- `docs/observability_dashboard` + - current north-star document for the lab/observability direction +- `docs/entrypoints.md` + - practical command/entrypoint map +- `tui.py` + - top-level CLI/parser and current main control surface +- `tui_app/app.py` + - TUI behavior, slash commands, transcript rendering, `/show` surfaces +- `tui_app/backends/` + - backend-specific runtime implementations +- `tui_app/transports/` + - shared protocol/client logic, especially OpenAI-compatible transport behavior +- `models///` + - model-first working area for notes, config, templates, prompts + +When orienting to a task: +- start with the smallest canonical document or module that explains the behavior +- prefer the model/backend-specific folder before searching the whole repo +- prefer decisions/specs over reverse-engineering behavior from scattered code when documentation exists + +## Current UX Posture +- The Textual TUI is a valid current control surface. +- The TUI is not the final observability or optimization product. +- Work that improves browser/dashboard-oriented observability is aligned with repo direction. +- Terminal UX should remain useful, but new designs should not assume the TUI is the only long-term interface. ## Current Entry Points -- `runner.py`: minimal interactive text generation runner. -- `chat.py`: template-aware chat loop with conversation history. -- `alex.py`: GGUF chat runner via `llama-cpp-python`. -- `ollama_chat.py`: Ollama API wrapper with optional reasoning filtering. +- **Unified TUI (current interaction surface):** + - `tui.py`: multi-backend Textual TUI + - `tui_app/`: internal package used by `tui.py` (don’t run directly) + +- **Managed backend control:** + - `vllm-up`, `vllm-down`, related repo helpers for managed vLLM bring-up + +- **Simple non-TUI CLIs (good for isolation/debug):** + - `chat.py`: HF/Transformers chat loop + - `runner.py`: minimal HF prompt -> completion loop + - `alex.py`: GGUF chat loop + - `ollama_chat.py`: Ollama streaming CLI ## Conventions -- Default to straightforward Python scripts over heavy framework structure. -- Keep dependencies minimal and rely on the existing `.venv`. -- Favor clear failure messages when CUDA/model loading is unavailable. -- Keep backend-specific logic separated instead of overloading one script. +- Prefer straightforward Python and small modules; avoid heavy frameworks unless they materially improve the lab. +- Keep backend-specific logic under `tui_app/backends/` and shared protocol logic under `tui_app/transports/` when applicable. +- Preserve consistent event behavior across backends: + - `TurnStart`, `ThinkDelta`, `AnswerDelta`, `Meta`, `Error`, `Finish` + - thinking routing via `ThinkRouter` and `assume_think` + - `/show` surfaces for effective/requested state when available + - `/show logs` via a per-session ring buffer +- Prefer runtime facts over intent-only displays. +- Distinguish clearly between: + - requested/configured values + - effective/runtime values + - unknown/unavailable values +- Standardize the UX shape where possible, but do not invent precision the backend cannot actually provide. +- Favor clear failure messages when CUDA/model loading/runtime support is unavailable. +- When backend behavior is discovered during debugging, prefer capturing it in notes, a decision record, or a spec instead of relying on memory. ## Usage -Run: +Recommended: +```bash +python tui.py [backend_hint] +python tui.py --config --backend [--profile ] +``` +Legacy/simple: ```bash python runner.py python chat.py @@ -33,4 +138,27 @@ python alex.py python ollama_chat.py ``` -Type prompts at `>` and type `exit` or `quit` to end. +## Repo Layout (model-first) +- Model assets live under `models///`: + - `notes/` for repo-local notes and upstream model cards + - `config/default.toml` and `config/profiles/*.toml` + - `templates/` for template overrides + - `prompts/` for prompt/system assets + +Use: +```bash +python scripts/model add [--id ] +``` +to scaffold new folders. + +## Direction +The repo is moving toward a stronger local observability story: +- better backend/runtime truth surfaces +- better comparison across engines +- browser/dashboard-oriented metrics and visualization +- a more complete local model lab feel + +The observability direction already has a home in: +- `docs/observability_dashboard` + +Keep that direction in mind when making UX or architecture decisions. diff --git a/README.md b/README.md index 82b8a08..500461b 100644 --- a/README.md +++ b/README.md @@ -1,80 +1,181 @@ # model-runner -Lightweight local model runner scripts for: -- Hugging Face text/chat models (`transformers` + `torch`) -- GGUF models via `llama-cpp-python` -- Ollama models via HTTP API with optional reasoning-filtered output -- EXL2 models via ExLlamaV2 (unified TUI backend) +`model-runner` is a local model lab: a practical workspace for running local models, comparing backends, tuning inference, and testing agent/application behavior without leaving a local-first environment. -## User Guide +This repo spans two overlapping jobs: +- runtime and backend engineering +- AI application, agent, and prompt experimentation -### 1) Setup +It is intentionally broad. It contains useful tooling, model-specific notes/configs/templates, backend adapters, and a growing observability direction for understanding what models are actually doing at runtime. -Use the existing virtual env in this repo: +## What This Repo Is +This project is for local model research and tinkering: +- run the same model across multiple backends +- compare runtime behavior instead of guessing +- keep model-specific notes, configs, templates, and prompts in-repo +- test tool use, thinking behavior, context management, and backend quirks +- build toward a real local observability and optimization workflow + +Supported runtime paths today include: +- Hugging Face / `transformers` + `torch` +- GGUF via `llama-cpp-python` +- Ollama via HTTP +- EXL2 via ExLlamaV2 +- OpenAI-compatible servers +- managed vLLM + +## Current State + +This repo is already useful, but uneven. + +What exists now: +- a multi-backend Textual TUI for local interaction and debugging +- simple CLI entrypoints for isolated backend checks +- model-first config/notes/templates layout under `models/` +- a lot of backend standardization work around prompts, knobs, history, logging, token accounting, and tool calls + +What is still rough: +- the TUI is a workable control surface, but not a polished observability product +- backend comparison is possible, but not yet clean or visual enough for serious runtime learning +- some runtime truths are still too hard to surface quickly + +If you want to just chat with models, the current tooling works. +If you want to learn how backends really behave, optimize them, and compare them with confidence, the repo still needs a stronger observability layer. + +## Layout Philosophy + +This repo remains model-first for authored metadata, but machine assets now live outside the repo. + +- repo `models/` = config, notes, prompts, templates, compressor recipes +- `~/models` = actual weights and quantized artifacts +- `~/data/model-runner` = durable input datasets +- `~/runs/model-runner` = telemetry, transcripts, logs, outputs, state + +Runtime artifacts should use logical run paths, not repo-path mirroring: + +- `~/runs/model-runner/logs////` +- `~/runs/model-runner/state////` +- `~/runs/model-runner/outputs/` +- `~/runs/model-runner/telemetry/` +- `~/runs/model-runner/transcripts/` + +See: +- [docs/reference/local-layout.md](/home/poop/code/dev/model-runner/docs/reference/local-layout.md) + +## Direction + +The direction is a **local model lab**, not “just a TUI.” + +That means: +- keep local-first model execution +- preserve backend diversity instead of collapsing everything into one engine +- make runtime behavior visible and measurable +- make tuning and comparison enjoyable, not forensic +- move toward a browser-based observability and control surface for metrics, backend state, cache growth, memory behavior, and experiment comparison + +The existing observability direction is captured in: +- [docs/observability_dashboard](/home/poop/ml/model-runner/docs/observability_dashboard) + +The current TUI should be thought of as: +- a current interaction surface +- a useful backend bring-up tool +- not the final home for optimization, inspection, and comparison UX + +## Entry Points + +### Current control surface: unified TUI + +Use `tui.py` when you want a terminal UI for chat, streaming, thinking display, slash-command inspection, or fast backend switching. + +Recommended environment setup: ```bash -source .venv/bin/activate -pip install -r requirements.txt +uv sync --python 3.12 ``` -Optional (recommended): install a console entrypoint for the unified TUI: +Optional backend groups: ```bash -pip install -e . +uv pip install --python .venv/bin/python exllamav2 +uv pip install --python .venv/bin/python llama-cpp-python ``` -Optional GGUF support: +Why the setup is serving-first: +- the main repo environment is anchored on `vllm>=0.18` +- the current Fedora target environment is a manual `uv` venv built around Python `3.12`, `vllm 0.18.0`, `torch 2.10.0+cu130`, `transformers 5.3.0`, and `huggingface_hub 1.7.2` +- managed repo launchers prepend the repo-local Torch and NVIDIA runtime libraries automatically so `./vllm-up` works without shell-level `LD_LIBRARY_PATH` setup +- released `llmcompressor` builds are currently incompatible with `vllm 0.18.x`, so compression is not part of the main sync +Examples: ```bash -pip install llama-cpp-python +.venv/bin/python tui.py Nanbeige4.1-3B +.venv/bin/python tui.py --config Qwen3.5-9B --backend vllm +.venv/bin/python -m vllm.entrypoints.cli.main serve --help ``` -Optional EXL2 support: +### Backend bring-up: managed vLLM helpers -- See `docs/exl2_setup.md` +Use the helper commands when you want repo-configured managed vLLM without entering the TUI: -### 2) Scripts and What They Do +```bash +./vllm-up --config Qwen3.5-9B +./vllm-up --config Qwen3.5-9B --bg +./vllm-down --config Qwen3.5-9B +``` -- `runner.py` - - Simple prompt loop for text generation. - - Optional token streaming with `--stream`. - - Supports `-8bit` / `-4bit` (bitsandbytes) on CUDA. - - Best for non-chat generation tests. +### Isolation/debug CLIs -- `chat.py` - - Chat loop with conversation history. - - Uses tokenizer native chat template by default. - - Optional `--prompt-mode plain` for minimal role-formatted prompting (no chat template). - - Decodes assistant-only new tokens. - - Optional token streaming with `--stream`. - - Supports precision selection via `--dtype`. - - Default `--max-new-tokens` is `2048`. - - Supports `-8bit` / `-4bit` (CUDA only). +Use these when you want simpler, backend-specific loops: +- `runner.py` + - minimal HF prompt -> completion +- `chat.py` + - template-aware HF chat loop +- `alex.py` + - GGUF chat via `llama-cpp-python` +- `ollama_chat.py` + - Ollama streaming chat loop - `tui_chat.py` - - Legacy Textual TUI for HF chat. - - Kept as migration reference. + - older HF-only Textual TUI kept as reference/debug aid -- `tui.py` - - Unified Textual TUI entrypoint for HF, GGUF, Ollama, and EXL2. - - Auto-detects backend from `model_id` or accepts `--backend`. - - Bottom grey input band with scrollable transcript above. - - Collapsible streaming thinking section per assistant turn. - - Hides `` markers while routing inner content to a grey “thinking” panel. - - Optional `--assume-think` / `--no-assume-think` for models that emit only end-think markers. +More detail lives in: +- [docs/entrypoints.md](/home/poop/ml/model-runner/docs/entrypoints.md) -- `alex.py` - - GGUF chat runner via `llama-cpp-python`. - - Accepts Windows or WSL paths. - - Best for direct `.gguf` testing without Ollama. +## Model-First Workspace -- `ollama_chat.py` - - Lightweight wrapper around Ollama `/api/chat`. - - Streams output and can hide reasoning blocks. - - Auto-detects Ollama host (`OLLAMA_HOST`, localhost, WSL gateway). +The repo is organized around models first, then backends: + +- `models///notes/` +- `models///config/` +- `models///templates/` +- `models///prompts/` + +That structure exists so research stays grounded: +- notes next to configs +- templates next to the model they affect +- backend variants for the same model stay comparable + +To scaffold a new model folder: +```bash +python scripts/model add [--id ] +``` -### 3) Common Commands +## Setup + +Use the repo `uv` environment: -Unified TUI (recommended): +```bash +uv sync --python 3.11 +``` + +Optional backend extras: +- GGUF: `uv sync --python 3.11 --group gguf` +- EXL2: see [docs/exl2_setup.md](/home/poop/ml/model-runner/docs/exl2_setup.md) +- EXL2 install: `uv sync --python 3.11 --group exl2` +- Compression is currently out-of-band until a released `llmcompressor` version coexists with `vllm 0.18.x` + +## Common Commands + +Unified TUI: ```bash tui Nanbeige4.1-3B tui /mnt/d/models/your-model.gguf @@ -82,15 +183,13 @@ tui ollama:your-ollama-model tui --backend exl2 /path/to/exl2_model_dir ``` -Hugging Face text run: - +HF text run: ```bash python runner.py Nanbeige4.1-3B python runner.py /home/poop/ml/models/Nanbeige4.1-3B -8bit ``` -Hugging Face chat run: - +HF chat run: ```bash python chat.py Nanbeige4.1-3B python chat.py Nanbeige4.1-3B --prompt-mode plain @@ -98,126 +197,82 @@ python chat.py Nanbeige4.1-3B --dtype bfloat16 python chat.py Nanbeige4.1-3B -4bit --dtype float16 --system "You are concise." ``` -Config-driven run (recommended): - +Config-driven run: ```bash python chat.py --config Nanbeige4.1-3B python chat.py --config models/Nanbeige4.1-3B/hf/config --max-new-tokens 1024 python chat.py --config Nanbeige4.1-3B --stream -python -m pip install -e . -tui Nanbeige4.1-3B -python tui.py Nanbeige4.1-3B -python tui.py /mnt/d/models/your-model.gguf -python tui.py /mnt/d/models/your-model.gguf --assume-think -python tui.py ollama:your-ollama-model -python tui.py ollama:your-ollama-model --backend ollama --ollama-think false -python tui_chat.py --config Nanbeige4.1-3B -python tui_chat.py --config Nanbeige4.1-3B --prompt-mode plain python runner.py --config Nanbeige4.1-3B -``` - -Template config: - -```bash -mkdir -p models/MyModel/hf/config -cp models/_TEMPLATE/hf/config/config.json models/MyModel/hf/config/config.json +python tui.py --config Nanbeige4.1-3B --backend hf ``` GGUF run: - ```bash python alex.py "/mnt/d/models/your-model.gguf" ``` -Ollama API run: - +Ollama run: ```bash python ollama_chat.py "your-ollama-model" --think false python ollama_chat.py "your-ollama-model" --think false --strict-think-strip ``` -### 4) Path Notes - -- Bare names like `Nanbeige4.1-3B` auto-resolve to `~/ml/models/` if present. -- Windows paths are accepted by `chat.py`, `runner.py`, and `alex.py` and mapped to WSL style when possible. -- In WSL, Windows drives are typically mounted under `/mnt//...`. - -### 5) Troubleshooting - -- `... is not a local folder and is not a valid model identifier` - - Use full local path or place model under `~/ml/models/`. - -- Tokenizer conversion errors - - Ensure: `sentencepiece`, `tiktoken`, `protobuf` are installed. - -- PersonaPlex model fails in `runner.py`/`chat.py` - - Expected: PersonaPlex is speech-to-speech, not a text `AutoModelForCausalLM` checkpoint. - -- Ollama connection refused from WSL - - `ollama_chat.py` auto-detects host each run. - - If needed: `python ollama_chat.py "" --host "http://:11434"`. - ## Configs -- Config files live under `models///config/`. -- Current starter profile: `models/Nanbeige4.1-3B/hf/config/config.json`. -- HF template profile: `models/_TEMPLATE/hf/config/config.json`. +- Config files live under `models///config/` - `--config` lookup supports: - direct file path - - path without `.json` - - directory containing `config.json` (e.g. `models//hf/config`) - - short name resolved under `models///config/config.json` (e.g. `Nanbeige4.1-3B`) + - path without extension + - directory containing config + - short name resolved under `models///config/` Precedence: - 1. CLI flags 2. Config values 3. Script defaults -Selected HF knobs now exposed in CLI/config: - -- sampling: `temperature`, `top_p`, `top_k`, `typical_p`, `min_p` -- output mode: `stream` -- routing mode: `assume_think` (TUI) -- length/termination: `max_new_tokens`, `max_time`, `stop_strings` -- repetition/structure: `repetition_penalty`, `no_repeat_ngram_size` -- decoding mode: `num_beams` -- prompt control: `system`, `system_file`, `user_prefix`, `prompt_prefix` -- template control (chat): `chat_template` (`default`, `search`, or template file path) -- chat memory control: `max_context_tokens` (chat only) - -## Contributor Guide - -### Repo Structure - -- `runner.py`: baseline text generation flow. -- `chat.py`: template-aware chat flow. -- `tui.py` + `tui_app/`: unified Textual TUI + backend adapters. -- `alex.py`: GGUF (`llama-cpp-python`) chat backend. -- `ollama_chat.py`: Ollama HTTP streaming backend + filtering. -- `models/`: model-first workspace for per-model config/notes/templates/prompts. -- `docs/`: decisions/specs/audits for repo evolution. -- `requirements.txt`: current `.venv` package lock-style snapshot. -- `models/_shared/`: shared templates and prompt assets (organized by backend). - -### Design Constraints - -- Keep scripts simple and local-first. -- Prefer explicit, readable CLI behavior over framework complexity. -- Do not mix speech-model pipelines into text runners. -- Keep error messages direct and actionable. - -### Development Workflow - -1. Edit one script at a time. -2. Run syntax checks: - - `python -m py_compile runner.py chat.py tui.py tui_chat.py alex.py ollama_chat.py` -3. Validate script help output: - - `python