* feat(model-tools): integrate hanzo-ml model soups, BitDelta, and GRPO preview
Surface the newly-landed hanzoai/ml features in the desktop:
- Rust: new `model_tools` module (Tauri commands) that shells out to the
hanzo-ml `soup` CLI (hanzo_ml::model_delta) for model soups (average/delta)
and 1-bit BitDelta encode/apply, mirroring how the inference engine
(mistralrs-server) is spawned. Binary resolved via HANZO_SOUP_BIN, a bundled
soup-<arch> next to the app, or PATH. Also a developer-preview GRPO entry
(model_tools_grpo_run) that runs `cargo run -p hanzo-training --example
grpo_toy` from HANZO_ML_DIR (grpo is library-only; no daemon yet).
- Frontend: lib/model-tools client (invoke wrappers + React Query hooks) and a
"Model Tools" tab in the Node Manager window (merge/compress + GRPO actions),
gated on soup availability.
- download-binaries.sh: fetch the soup CLI (graceful fallback) and pin engine
v1.0.2 / hanzo-ml v0.9.2 versions.
- Docs: refresh LLM.md Model Tools / GRPO status.
* fix(ci): fetch prebuilt hanzo-node binary in E2E instead of building from source
The E2E "Build hanzo-node (Rust backend)" step checked out hanzoai/node@main
and ran `cargo build --release --bin hanzo-node`. That build fails on main:
hanzoai/node's workspace Cargo.toml lists ~35 hanzo-libs/* path-members
(hanzo-api, hanzo-config, ...) but only hanzo-brain/hanzo-pqc are vendored in
the repo tree, so cargo aborts with:
failed to load manifest for workspace member .../hanzo-libs/hanzo-api
(The bin target is also named `hanzod`, not `hanzo-node`, so `--bin hanzo-node`
would fail even if the workspace resolved.)
Switch the E2E job to download the published hanzo-node binary from the Hanzo
CDN — the same artifact ci-scripts/download-side-binaries.ts uses for the
release-prod and pr-ci-healthchecks workflows. This removes the external-repo
checkout + source build entirely and lays the binary out at hanzo-node/dist/
(matching the layout the start/verify steps already expect). Falls back to
latest.zip when the pinned HANZO_NODE_VERSION is not published. The Rust cache
is repointed at the Tauri workspace (apps/hanzo-desktop/src-tauri), the only
Rust still built in this job.
---------
Co-authored-by: Hanzo Dev <[email protected]>
5.2 KiB
Hanzo Desktop
Project
Desktop AI agent app. React + Vite + Tauri (Rust backend).
Structure
apps/hanzo-desktop/
src/ # React frontend
src-tauri/ # Rust Tauri backend
public/ # Static assets
libs/
hanzo-ui/ # Shared UI components
hanzo-i18n/ # Internationalization
hanzo-message-ts/ # Message/job API types
Commands
npx nx serve hanzo-desktop # Dev (Tauri, uses tauri.conf.local.json)
npx nx build hanzo-desktop # Production build
npx nx test hanzo-desktop # Vitest
npx nx lint hanzo-desktop # ESLint
npx nx run hanzo-desktop:typecheck
npx nx run hanzo-desktop:cargo-check
Alternative: cd apps/hanzo-desktop && npm run dev|build|lint
Code Style
- 2 spaces, Prettier + Tailwind plugin, single quotes
- Files/dirs: kebab-case. Components/types: PascalCase. Vars/functions: camelCase
- Tests: Vitest + @testing-library/react, co-located in
__tests__/,*.test.ts(x) - Commits: Conventional (
feat:,fix:,chore:,docs:)
Tooling
- Node 20+, npm 10+, Rust toolchain for Tauri
postinstallrunspatch-package- Env: copy
.env.example, never commit secrets
Release Pipeline
Workflows
- Production:
.github/workflows/release-prod.ymlon tags[0-9]+.[0-9]+.[0-9]+ - Development:
.github/workflows/release-dev.ymlon tagsdev-v[0-9]+.[0-9]+.[0-9]+
Required Secrets
Apple (macOS signing): APPLE_CERTIFICATE, APPLE_CERTIFICATE_PASSWORD, APPLE_SIGNING_IDENTITY, APPLE_ID, APPLE_PASSWORD, APPLE_TEAM_ID, KEYCHAIN_PASSWORD
Tauri updates: TAURI_PRIVATE_KEY, TAURI_KEY_PASSWORD
Windows EV (Google Cloud KMS): EV_CODE_SIGNING_CERTIFICATE
Infrastructure: R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, SLACK_BOT_TOKEN
App: VITE_POSTHOG_API_KEY, SECRET_DESKTOP_INSTALLATION_PROOF_KEY
Build Artifacts
- Installers: .AppImage (Linux), .dmg (macOS), .exe (Windows)
- Update bundles: .tar.gz (macOS/Linux), .nsis.zip (Windows)
- Signature files: .sig for Tauri auto-updater
- Uploaded to: GitHub Releases + Cloudflare R2 (download.hanzo.ai)
- Rollout: rename
updates-next.jsontoupdates.json
Troubleshooting
- APPLE_CERTIFICATE must be base64 without newlines
- Use
base64 --decode > filenotbase64 --decode -o file - Ensure package.json version matches git tag
- Update check failures are handled gracefully (no user-facing errors)
Model Tools (hanzo-ml integration)
The Node Manager window has a Model Tools tab (components/hanzo-node-manager/model-tools.tsx)
that drives the hanzoai/ml features via the Tauri backend (src-tauri/src/model_tools):
- Model soups + BitDelta — fully wired. Shells out to the
hanzo-mlsoupCLI (hanzo_ml::model_delta):average(uniform/weighted soup),delta(base + mean fine-tune delta),bitdelta-encode/bitdelta-apply(1-bit delta compress/restore). Binary resolved viaHANZO_SOUP_BIN, a bundledsoup-<arch>next to the app, orsoupon PATH. The UI gates on availability. - Frontend:
lib/model-tools/model-tools-client.ts(invoke wrappers + React Query hooks). Tauri commands:model_tools_soup_*,model_tools_bitdelta_*.
Machines (Docker Desktop replacement)
Desktop UI for Linux VMs (vfkit) and the docker / k3s stack served by the
hanzod daemon in hanzoai/node/machine/. Hanzo Desktop is a drop-in
all-in-one Docker Desktop replacement: containers, Kubernetes, and Linux
machines from one app, one binary backend.
apps/hanzo-desktop/
src-tauri/src/machine/ # Tauri commands -> unix socket client
socket.rs # ~/.hanzo/run/machined.sock resolver
types.rs # mirrors machine/proto.go (Machine, ExecFrame, ...)
src/pages/machines/ # React UI (list, create dialog, exec stream)
- Tauri commands:
machine_health,machine_list,machine_create,machine_start,machine_stop,machine_delete,machine_exec(streaming). - Wire format is JSON over the daemon's UNIX socket - see
hanzoai/node/machine/proto.gofor the authoritative contract. - Brand gating: the Machines / Containers / K8s pages are Hanzo-only by
default. Other brands (zoo, liquidity) must set
VITE_ENABLE_MACHINES=true(or the equivalent runtime flag inlibs/brand-config) to reveal them. Seelibs/brand-config/src/index.ts::isMachinesEnabled.
e2e
e2e-tests/machine-flow.spec.ts runs against pnpm dev, mocks
window.__TAURI_INTERNALS__.invoke for the machine_* commands, and asserts
the create dialog round-trip. CI must pass VITE_BRAND=hanzo so the
Machines route renders.
GRPO Status
Trainer-free GRPO lives in hanzo_training::grpo (a Rust library + a runnable
grpo_toy example) — there is no GRPO server/daemon yet. The desktop exposes a
developer-preview entry (model_tools_grpo_run, surfaced in the Model Tools
tab) that runs cargo run -p hanzo-training --example grpo_toy from a local
checkout (HANZO_ML_DIR). This is plumbing/demo only.
Productizing GRPO in-app still needs: experience/trajectory storage, agent
versioning / A/B testing, a real training job pipeline (driving the Policy
trait on a packaged model), feedback→reward mapping, and ML metrics. Foundation
exists (agent CRUD, message history with tool metadata, Zustand, React Query).