Skip to content

feat(agent): local-model room agent (Ollama -> GroupMind, offline-capable)#39

Merged
ThinkOffApp merged 3 commits into
mainfrom
feat/local-agent
Jul 16, 2026
Merged

feat(agent): local-model room agent (Ollama -> GroupMind, offline-capable)#39
ThinkOffApp merged 3 commits into
mainfrom
feat/local-agent

Conversation

@ThinkOffApp

Copy link
Copy Markdown
Owner

What

scripts/local-agent.mjs — puts a local Ollama model into a GroupMind room. It polls the room and, on an @mention of its handle, prompts the local model over Ollama's HTTP API and posts the reply back, so an on-device model is a first-class room participant.

It reads/writes cloud GroupMind when the net is up and the LAN GroupMind Local relay when it isn't — point mm + mb agents at the same relay, flip the net off, and the local pair keeps talking. That's the offline-fleet demo.

Details

  • Zero deps (global fetch + built-ins). npm run agent.
  • History-skip on first poll; never replies to itself; mention-gated by default (respondTo:'all' optional).
  • 5 unit tests (mention detection, self/history guards, respond-all, the Ollama call against a mock). Full suite 155 green.

Verified live

qwen3.5:9b on the M4 Mini (16.6 tps) introduced itself in the room. Model note: a 27B is not viable on 24GB — GGUF 4.9 tps + RAM-tight, and the MLX build won't load (needs 18.4GB, 17.3 free). So the Mini runs a right-sized fast model; the 128GB MacBook carries the big SOTA one.

Not in this PR

  • Own-handle provisioning (the agent currently needs a handle+key; owned-agent mint endpoint 401s in prod — pending that deploy or manual provisioning). Until then a host relays its model's output.

Cross-review welcome @codexmb / @claudemb.

🤖 Generated with Claude Code

… failover)

Puts an on-device (Ollama) model into a GroupMind room: polls the room, and on
an @mention prompts the local model and posts the reply back — so a local model
becomes a first-class room participant. Reads/writes cloud GroupMind when the
net is up and the LAN GroupMind Local relay when it isn't, so point mm + mb
agents at the same relay, flip the net off, and the local pair keeps talking.

Zero deps. History-skip on first poll, never replies to itself, mention-gated by
default. 5 unit tests (mention/self/history/respond-all + Ollama call). Suite 155 green.

Verified live: qwen3.5:9b on the M4 Mini (16.6 tps) introduced itself in the room.
Note: a 27B is too big for 24GB here (GGUF 4.9 tps + RAM-tight, MLX won't load).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 15, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b10ea3eeab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/local-agent.mjs

async function postMessage(ep, room, body) {
const res = await fetch(`${ep.baseUrl.replace(/\/$/, '')}/messages`,
{ method: 'POST', headers: headersFor(ep), body: JSON.stringify({ room, body }), signal: AbortSignal.timeout(8000) });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the sender when posting relay replies

In offline/relay mode with respondTo: 'all', this POST omits from: cfg.handle, so scripts/local-relay.mjs stores the agent's own replies as from: "unknown"; on the next poll they no longer match sameHandle(m.from, cfg.handle) and the agent replies to its own previous relay message indefinitely. Include the configured handle when posting to the local relay so the self guard still works.

Useful? React with 👍 / 👎.

ThinkOffApp and others added 2 commits July 15, 2026 13:21
The fleet's local models are reasoning models (the Mini runs qwen3.6:35b-a3b MoE
at UD-Q2_K_XL, load-tested at 31.7 tps on the M4), so generateReply now strips
<think>...</think> so room replies read clean. Default OLLAMA_MODEL updated to
the MoE tag. +1 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Q3_K_M is the Mini pick: clean output (fixes Q2's untagged reasoning-leak),
22.6 tps, fits 24GB (tight). Added a keep_alive (default 5m) so the 17GB model
unloads when idle instead of pinning RAM the Mini's other services need.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ThinkOffApp ThinkOffApp merged commit 7075984 into main Jul 16, 2026
3 checks passed
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