Files
desktop/LLM.md
a194d008d6 fix(ci): fetch prebuilt hanzo-node binary in E2E instead of building from source (#8)
* 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]>
2026-06-17 23:59:53 -07:00

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
  • postinstall runs patch-package
  • Env: copy .env.example, never commit secrets

Release Pipeline

Workflows

  • Production: .github/workflows/release-prod.yml on tags [0-9]+.[0-9]+.[0-9]+
  • Development: .github/workflows/release-dev.yml on tags dev-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.json to updates.json

Troubleshooting

  • APPLE_CERTIFICATE must be base64 without newlines
  • Use base64 --decode > file not base64 --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-ml soup CLI (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 via HANZO_SOUP_BIN, a bundled soup-<arch> next to the app, or soup on 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.go for 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 in libs/brand-config) to reveal them. See libs/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).