# ZeroLM

ZeroLM is a zero-cost local language-model laboratory for a 24 GB Apple Silicon
Mac. Daily chat uses Qwen's official Apache-2.0 `Qwen3-4B-MLX-4bit` weights.
Photo questions use a pinned 4-bit Qwen3-VL conversion through MLX-VLM.
Checked arithmetic stays independent from model guesses, while broader math
questions use a pinned 4-bit DeepSeek-R1-Distill-Qwen-7B conversion. The
original Chinese-first,
from-random-weights PyTorch path remains available for controlled experiments.

This is not GPT-5.6 and does not use a paid API. The open model is materially
more useful than the tiny from-scratch checkpoint, but it still does not
approach frontier hosted models.

## Quick start

```bash
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.lock
.venv/bin/python -m pip install -e . --no-deps
.venv/bin/zerolm doctor
.venv/bin/python -m pytest
.venv/bin/zerolm demo --max-steps 2
```

For the recommended MLX assistant path, install the separately pinned stack:

```bash
uv pip install --python .venv/bin/python -r requirements-mlx.lock
.venv/bin/python -m pip install -e . --no-deps
.venv/bin/zerolm open-doctor
.venv/bin/zerolm open-generate --message '你好，请介绍自己。'
```

The first generation downloads about 2.2 GB; later generation is local. The
app reloads the model for each answer but restores its private local chat
archive and passes a bounded selection of recent and relevant text into that
process. See [`docs/open-model.md`](docs/open-model.md) for the pinned revision,
official sources, measured resources, and the tested LoRA workflow.

For photo and camera questions, install the complete pinned vision stack:

```bash
uv pip install --python .venv/bin/python -r requirements-vision.lock
.venv/bin/python -m pip install -e . --no-deps
.venv/bin/zerolm vision-doctor
.venv/bin/zerolm vision-generate \
  --image outputs/ZeroLM-icon.png \
  --message '请描述这个图标。'
```

The pinned vision snapshot is about 3.11 GB. It is already cached on the
verified development Mac; otherwise the first image request downloads it from
Hugging Face. Selected images are decoded, stripped of metadata, corrected for
orientation, reduced to at most 1,048,576 pixels, and processed locally.

Vision prompts now require the model to identify people, animals and objects
separately before answering. When a person and a dog appear together, image
generation prompts explicitly preserve two distinct subjects and forbid merged
human/canine anatomy. This reduces a reported class-confusion failure, but it
does not make either model infallible.

For local neural speech with Chinese and English voice choices:

```bash
uv pip install --python .venv/bin/python -r requirements-voice.lock
```

The desktop app uses the Apache-2.0
`mlx-community/Qwen3-TTS-12Hz-0.6B-CustomVoice-4bit` model through MLX-Audio.
The first download is cached; later playback is local. Six included presets
cover four Mandarin voices and two English voices. This path does not use
Apple's `AVSpeechSynthesizer`.

The demo trains on a tiny original corpus bundled with this repository and
writes ignored artifacts under `artifacts/demo/`. Its generated text will be
mostly nonsense after two steps; a checkpoint plus generated output proves the
mechanics work.

## macOS app

Build the native SwiftUI desktop app and its locally drawn icon:

```bash
macos/build_app.sh
open outputs/ZeroLM.app
```

产品网页提供 Mac 安装包和 Windows 文字聊天预览包。微信窗口分析需要在
macOS“隐私与安全性”中授予 ZeroLM 屏幕录制权限，只读取当前可见窗口，
截图不会保存；“持续分析”关闭后立即停止。

Version 2.0 provides a native conversation sidebar, searchable/date-grouped
history, a focused chat workspace, rich Markdown and code blocks, safe
import/export (including round-trip `.md` conversation backups), a five-part
settings center, glass model controls, glass chat
bubbles, “拍照”/“选择照片” controls, photo drag and drop, and local image replies.
Camera permission is requested only after “拍照” is pressed; selected or
captured images are not sent to a paid API.

The answer-mode menu now also includes a bounded “本地 Agent”. Before using
it, select one workspace under Settings → Connections & Agent. Its tools can
list, search and read text files only inside that directory. Symlinks, hidden
paths, files above the read limits and every request to delete, move or
overwrite data are rejected before the language model sees the tool result.

Settings → Connections & Agent also contains a read-only WeChat export
inspector. It accepts only a user-selected `.txt`, `.md` or `.json` export up
to 25 MB and reports duplicate, empty and link-only messages. It never opens,
decrypts or modifies WeChat databases. WeChat does not provide this project
with a public personal-chat deletion interface, so deletion of real chats
remains an explicit action inside WeChat.

Normal answers can optionally use live web search and Qwen's deep-thinking
mode. Thinking tokens appear as they are generated and remain collapsible
after completion. Every assistant response includes copy, regenerate and
local neural-voice controls. Voice, speaking speed and optional automatic
playback are configured under Settings → Answers.

The glass “记忆篮子 🧺” opens persistent memory settings. Chat text, the
on/off setting, and up to 4,000 characters of user-written memory survive app
restarts. Turning memory off keeps the visible chat archive but prevents the
model from receiving earlier messages or custom memory; the editor becomes
disabled and gray. When memory is on, custom memory is always included and
ZeroLM searches the complete retained archive for relevant older messages,
then combines them with recent conversation under the model's bounded context.

The workspace keeps up to 50 conversations and 2,000 text messages in
`~/Library/Application Support/ZeroLM/workspace-state-v1.json`. The previous
single transcript migrates automatically. Its directory and
file use `0700` and `0600` permissions. Photo bytes and paths are never written
to the archive. The per-answer model history remains a private temporary file
that is removed after the process finishes. Do not place passwords or secret
keys in custom memory. The visible app and chat identify every route simply as
ZeroLM; upstream names remain in this technical documentation for license and
provenance accuracy.

The three visible modes are:

- “正常回答”: pinned free Qwen3-4B, about 2.2 GB on first download.
- “算术题”: exact arithmetic first, then pinned free DeepSeek-R1-Distill-
  Qwen-7B 4-bit for broader problems, about 4.3 GB on first download.
- “我们的模型”: the real from-random-weights ZeroLM checkpoint, about 61 MB
  and intentionally labeled as a weak experiment.

## iPhone app (Xcode Beta)

The native iPhone app is in `ios/ZeroLMMobile.xcodeproj`. It runs
Qwen2.5-1.5B-Instruct locally through llama.cpp. It supports iOS 16.4+,
including iPhone X, 11, 12 and newer, and does not require Apple Intelligence
or a paid API for text chat. An optional private Account Hub on the Mac adds
same-account desktop conversation viewing, paired phone Agent tasks and six
ZeroLM neural voices without Apple's system speech engine.

On first use, Settings downloads the approximately 1.12 GB Q4_K_M model and
verifies its SHA-256 digest. After that, text generation works offline. Chat
history and the memory basket remain in the app's protected container. The
current model is text-only, so the camera/photo controls do not yet perform
image understanding. See [`ios/README.md`](ios/README.md) for installation,
model provenance and verification.

In “算术题”, signed integer expressions using `+`, `-`, `*`, `/`, and
parentheses go to the restricted exact evaluator, never Python `eval`.
Division by zero is returned as a verified undefined result instead of being
guessed. Unsupported math prompts fall back to DeepSeek and are explicitly
marked unverified. Daily text uses the pinned Qwen3-4B base model; experimental
smoke LoRA adapters are never auto-loaded. Every backend shares the ZeroLM
assistant identity in chat; this documentation retains the required upstream
attribution. First use may download the relevant model from Hugging Face;
cached inference then runs through local MLX. The app is tied to this project
directory, so keep the project and its `.venv` in place.

The old from-scratch interactive checkpoint remains available for comparison:

```bash
.venv/bin/zerolm chat \
  --device mps \
  --checkpoint artifacts/demo/checkpoints/final.pt \
  --tokenizer artifacts/demo/tokenizer.model
```

Type `/quit` to exit. The bundled question-answer corpus is deliberately tiny,
so this mode can answer only a small set of simple questions reliably.

## Product website

The static product site lives under `web/`:

```bash
.venv/bin/python -m http.server 4173 --directory web
```

Open `http://127.0.0.1:4173/`. It includes responsive desktop/mobile layouts,
reduced-motion and reduced-transparency fallbacks, a safe Agent demonstration,
and a browser-local WeChat export demonstration. The page does not upload the
selected file.

The strongest verified zero-cost local checkpoint uses the `assistant` preset:

```bash
.venv/bin/zerolm demo \
  --device mps \
  --preset assistant \
  --vocab-size 2048 \
  --max-steps 1200
```

It has about 5.3 million parameters and basic memorized responses in Chinese,
English, Spanish, French, German, Portuguese, Japanese, Korean, Russian, and
Arabic. This is language exposure, not general fluency.

## Reasoning experiment instead of memorization

The `reasoning-data` workflow generates worked column-addition examples from
code. Training and benchmark pairs are unique and disjoint. The model earns
credit only when its final answer exactly matches an unseen problem; plausible
wording receives no partial credit.

```bash
.venv/bin/zerolm reasoning-data \
  --output-dir data/reasoning \
  --training-examples 100000 \
  --validation-examples 2000 \
  --max-digits 3

.venv/bin/zerolm tokenizer \
  --input data/reasoning/corpus.txt \
  --output artifacts/reasoning/tokenizer \
  --vocab-size 128 \
  --model-type char

.venv/bin/zerolm prepare \
  --input data/reasoning/corpus.txt \
  --tokenizer artifacts/reasoning/tokenizer.model \
  --output-dir data/reasoning/processed \
  --validation-fraction 0.0196078431372549

.venv/bin/zerolm train \
  --device mps \
  --preset assistant \
  --tokenizer artifacts/reasoning/tokenizer.model \
  --data-dir data/reasoning/processed \
  --output-dir artifacts/reasoning/checkpoints \
  --max-steps 100000 \
  --batch-size 32 \
  --learning-rate 0.0005 \
  --eval-interval 1000 \
  --checkpoint-interval 5000

.venv/bin/zerolm reasoning-eval \
  --device mps \
  --checkpoint artifacts/reasoning/checkpoints/final.pt \
  --tokenizer artifacts/reasoning/tokenizer.model \
  --benchmark data/reasoning/benchmark.jsonl
```

This tests narrow arithmetic generalization, not human-like thought or general
intelligence. A zero score is a valid experimental result; it must never be
hidden behind canned answers.

## Train on your own legally usable text

Place one or more UTF-8 text files under `data/raw/`, with one paragraph or
sample per line. Then run:

```bash
.venv/bin/zerolm tokenizer \
  --input data/raw/corpus.txt \
  --output artifacts/tokenizer \
  --vocab-size 4096

.venv/bin/zerolm prepare \
  --input data/raw/corpus.txt \
  --tokenizer artifacts/tokenizer.model \
  --output-dir data/processed

.venv/bin/zerolm train \
  --tokenizer artifacts/tokenizer.model \
  --data-dir data/processed \
  --preset smoke \
  --max-steps 1000

.venv/bin/zerolm generate \
  --checkpoint artifacts/checkpoints/final.pt \
  --prompt '语言模型'
```

Use only text you own, public-domain text, or material whose license permits
model training. Raw data, processed arrays, tokenizers, and checkpoints are
ignored by Git.

## Presets

- `smoke`: roughly 0.5M parameters with a 4K vocabulary; pipeline checks.
- `assistant`: roughly 5.3M parameters with a 2K vocabulary; strongest preset
  verified on the target M5 within the zero-cash workflow.
- `tiny`: roughly 11M parameters; the first meaningful local experiment.
- `small`: roughly 30M parameters; benchmark memory and speed before a long run.

Actual counts vary with tokenizer vocabulary size. Start with `smoke`, measure,
then scale. Training quality also requires far more and better data than the
bundled demo.

## Design sources

- PyTorch documents `mps` as its Metal-backed accelerator for macOS:
  https://docs.pytorch.org/docs/main/notes/mps.html
- Causal attention uses PyTorch's documented scaled dot-product attention:
  https://docs.pytorch.org/docs/main/generated/torch.nn.functional.scaled_dot_product_attention.html
- SentencePiece trains directly from raw sentences and is suitable for Chinese:
  https://github.com/google/sentencepiece

The detailed scope is in [`docs/spec.md`](docs/spec.md), and the implementation
checklist is in [`tasks/todo.md`](tasks/todo.md).
