Skip to content

hive/apple-fm: Apple's on-device model as a nurse - #71

Merged
adiled merged 5 commits into
mainfrom
hive/apple-fm-worker
Nov 1, 2026
Merged

adiled merged 5 commits into
mainfrom
hive/apple-fm-worker

Conversation

@adiled

@adiled adiled commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

hive/apple-fm: Apple's on-device model as a nurse

hives/apple-fm runs Apple's on-device foundation model (SystemLanguageModel.default, ~3B, 4096-token context) as a hum nurse: local, network-free, key-free. It advertises apple-3b-on-device.

This supersedes the Oct 18 apple-fm-worker prototype, which predates main's move from WorkerBee to Nurse (chunk/finish → unfurl/wilt, serve_worker → serve_nurse, signed bee entry, hive-install units, version lockstep) and could not compile against main. It is rebuilt on main, and hives/apple-fm-worker is removed.

Shape

  • apple-fm-nurse (Rust, on hum-nest's serve_nurse) runs apple-fm-gen once per bloom. apple-fm-gen is a Swift bridge built from bridge/gen.swift, the only file in hum that imports an Apple framework. The Rust side holds no Apple symbols and builds anywhere; ./build refuses any host that isn't an Apple silicon Mac.
  • How a prompt maps to Apple's API:
    • the system prompt becomes Apple's instructions
    • the graft becomes Apple's transcript; with no graft, the cell's own turns are used, so graft-less foragers keep their conversation
    • temperature/season/prune/seed become Apple's generation options; temperature: 0 samples greedily
    • frost (stop sequences) is held by the nurse, which kills the bridge at a stop
  • Streaming: each unfurl carries only the new words, and the bloom wilts stop or max_tokens with Apple's token counts as nectar.
  • Context window: the bridge keeps the newest turns that fit and holds the reply to the room left. The cell forgets the dropped turns, and the nurse logs apple-fm.trimmed.
  • Availability gate: before nestling, the nurse probes the bridge.
    • Apple Intelligence off, or model still downloading: waits unnestled and re-probes.
    • Mac that can't run the model: exits 0, so the unit stays down.
    • Bridge that can't run: exits non-zero.
  • Refusals and errors:
    • pollen → apple_fm_words_only; trellis → apple_fm_no_trellis
    • Apple's own errors → apple_fm_<reason>
    • a bridge that dies or goes silent → apple_fm_bridge_died

Bugs in the prototype, fixed

  1. The bridge's stdout was never piped.
  2. --probe was ignored by the Swift side, so the probe always reported "available".
  3. Cumulative snapshots were sent as deltas.
  4. A duplicate resolve_bridge broke non-mac builds.
  5. The cell's exit sender was dropped at birth.
  6. A cancel left the bridge running.
  7. The graft never reached the model.
  8. Error JSON broke on newlines.

Tests (cd hives/apple-fm && cargo test)

  • 17 unit and cell tests against a stand-in bridge. They cover the request mapping, sampling, refusals, Frost across delta boundaries, history kept and overridden by graft, dropped turns forgotten, still/cancel killing the bridge, stops, max_tokens, and a bridge that errors, dies, lingers, or goes silent.
  • 5 E2E tests through a real in-process humd:
    • a prompt unfurls and wilts back with nectar
    • a graft-less follow-up keeps the conversation
    • a cancel kills the bridge
    • a dormant nurse waits, then nestles when ready
    • an ineligible Mac exits 0
    • a missing bridge fails the unit
  • 1 live test against Apple's model (./build && cargo test --test live -- --ignored): conversation memory, real streaming with no repeated text, stop sequence, max_tokens at exactly the season, a conversation longer than the context, and cancel.
  • All green 5× in a row on an M5 Max running macOS 26.6.2.
  • Clippy: 0 findings in the hive.

Verified on a live humd

Installed with hum hive ./hives/apple-fm install (orchd → launchd unit hum-apple-fm.apple-fm) and exercised through openai-server's /v1/chat/completions:

  • system prompt
  • multi-turn
  • max_tokens → length
  • stop
  • SSE streaming
  • a conversation past 4096 tokens
  • a client disconnect killing the bridge immediately

Also checked against the real model:

  • no rate-limiting when launchd runs it
  • 8 concurrent prompts all succeed (the default maxCells)

Registered

  • root Cargo.toml exclude list
  • release.sh version lockstep (0.34.1)
  • hives/README.md catalogue

Not in this PR

  • Co-installation into the mac desktop bundle: the follow-up gate. Struck: there is no mac desktop bundle; after merge, apple-fm is a bundled hive any capable Mac installs with hum hive apple-fm install.
  • Tools and trellis. FoundationModels supports both, but the bridge offers neither yet; the README says so.

Note: wire.yml builds no hives, so CI doesn't exercise this hive. The tests above were run locally.

A sibling of ollama-worker that runs Apple's on-device 3B FoundationModel
via FoundationModels — a local, network-free, key-free LLM. Registers with
humd over thrum, advertises model `apple-3b-on-device`, and turns
chi:"prompt" into chunk/finish tones.

The Rust side (src/) mirrors ollama-worker but, instead of HTTP to Ollama,
shells out to a small Swift bridge (bridge/gen) that wraps FoundationModels
and streams NDJSON deltas. The only Apple-specific code in the tree is
bridge/gen.swift (+ probe.swift); no Apple symbols enter the Rust side,
and the bridge is mac-gated. On boot a capability probe keeps the hive
present-but-dormant when Apple Intelligence isn't available.

Isolated prototype — verified in isolation on macOS 26 (M5 Max): probe
reports available, real on-device deltas stream, and the Rust worker
compiles + connects to a live humd advertising the model. Not yet
co-installed into the mac desktop bundle.
…file

Rebuilds the apple-fm-worker prototype on main's nurse model as hives/apple-fm.
apple-fm-nurse (Rust, on hum-nest's serve_nurse) runs apple-fm-gen, a Swift
bridge to FoundationModels, once per bloom: the prompt's system prompt becomes
Apple's instructions, its graft (or the turns its cell grew, for a forager that
sends only new words) becomes the transcript, and temperature/season/prune/seed
become Apple's generation options. Deltas unfurl as they come, each only the new
words; the bloom wilts stop or max_tokens with Apple's token counts as nectar.
frost is held by the nurse, which kills the bridge at a stop; a cancel kills it
too. Pollen and trellis are refused rather than grown as if absent.

Before nestling it probes the bridge: it waits unnestled while Apple Intelligence
is off or its model downloads, exits 0 on a Mac that cannot run it (its unit stays
down), and fails on a bridge that cannot run.

Fixes in the prototype: the bridge's stdout was never piped, --probe was ignored by
the Swift side, cumulative snapshots were sent as deltas, a duplicate fn broke
non-mac builds, the cell's exit sender was dropped at birth, a cancel left the
bridge running, and the graft never reached the model.

Tests: unit and cell tests against a stand-in bridge; E2E through a real humd
(prompt, kept conversation, cancel, dormant-then-ready, never, missing bridge);
and an ignored live test against Apple's model.

Registered in the workspace exclude list, release.sh's lockstep, and the hives
catalogue.
…rom its newest turns

Apple's model has a 4096-token context, and a cell that remembers its sigil's
turns would otherwise fail every prompt once the conversation outgrew it. The
bridge now counts tokens (macOS 26.4+) and keeps the newest turns that leave
room for the reply (its season, else 1024 tokens), reporting how many it
dropped in its finish; the cell forgets the same turns. A reply with no season
is held to the room left, so it wilts max_tokens instead of overflowing
mid-stream, and the finish carries the bridge's own reason. Before 26.4 the
bridge drops the older half of the turns whenever Apple refuses the transcript.

The cell no longer waits on a bridge that lingers after its finish or error:
it kills it. Tests cover the dropped turns, the bridge's reason, a lingering
bridge, and, live, a conversation past the context.
…fits its context

A bridge that closed its stdout but lived on held its cell's next prompt
forever while the nurse waited on it; the nurse now gives it two seconds and
kills it, telling the bloom apple_fm_bridge_died. And a season larger than the
room the conversation left is held to that room too, so no reply overflows the
model's context mid-stream.
@adiled adiled changed the title hive: add apple-fm-worker (Apple on-device 3B as a WorkerBee) hive/apple-fm: Apple's on-device model as a nurse Nov 1, 2026
@adiled
adiled merged commit e4d5f4a into main Nov 1, 2026
7 checks passed
@adiled
adiled deleted the hive/apple-fm-worker branch November 1, 2026 14:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant