Skip to content

Multi-model LLM load balancer + describe-it-in-English rsync - #7

Merged
kochj23 merged 2 commits into
mainfrom
feature/llm-load-balancer-nl-rsync
Aug 20, 2026
Merged

Multi-model LLM load balancer + describe-it-in-English rsync#7
kochj23 merged 2 commits into
mainfrom
feature/llm-load-balancer-nl-rsync

Conversation

@kochj23

@kochj23 kochj23 commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Summary

Adds the shared multi-model LLM load balancer to RsyncGUI plus a new natural-language → rsync assistant.

Multi-model load balancing

Ported the pure/network-free pieces from AIStudio verbatimModelRegistry (DiscoveredModel, Ollama/MLX/frontier discovery, assemblePool), LoadBalancer (round-robin / least-busy + health gating), OpenRouterProvider (+ OpenAICompatibleRequest), and KeychainStore. LLMLoadBalancerService mirrors AIStudio's balanced-dispatch wiring: three persisted toggles, health-gated failover, least-busy dispatch.

  • Nova is never a hard requirement. Works with zero Nova on local models (Ollama /api/tags + MLX from the HF hub cache) and/or a frontier OpenRouter key (stored in the macOS Keychain).
  • Nova Gateway is one optional OpenAI-compatible backend (http://127.0.0.1:18792, health /v1/models). A failed health check just marks it unavailable; everything else keeps working. No dependency on Nova / PostgreSQL / the gateway.
  • Three toggles (All local / All frontier / Nova Gateway) are persisted and surfaced in Settings → AI Assist, along with endpoints, the OpenRouter key, and live backend status.

Describe-it-in-English rsync

The Job Editor's Basic tab gains an intent field. Describe a sync in plain English (e.g. "mirror Photos to the NAS, skip video files, delete extras on the destination") and the balanced LLM proposes a concrete rsync command, shown for review and used to pre-fill the command builder.

Safety — never auto-executed. rsync is destructive (--delete), so the suggestion is only displayed and used to populate the builder; the user must explicitly start the job.

How parseRsyncSuggestion prevents command injection

A pure, network-free validator extracts only a valid rsync invocation and rejects everything else:

  • A quote-aware tokenizer rejects any unquoted shell metacharacter — chaining (; && || | &), command substitution (` $( ${), grouping (() {}), redirection (> <) — and rejects $/backtick even inside double quotes, plus unbalanced quotes.
  • The program token must be rsync (bare or a path ending in /rsync); anything else (cp, sudo …, rm …) is rejected.
  • Every flag must be on a strict allow-list; program-executing flags (-e, --rsh, --rsync-path) are absent, so they are rejected.
  • If no backend is enabled the feature disables itself with a clear reason instead of crashing.

Sandbox

The app target's .entitlements already sets com.apple.security.app-sandbox = false (Hardened Runtime / notarization retained; not App Store). Confirmed disabled — required for local backend discovery and process-based MLX inference.

Tests

  • LoadBalancerTests — network-free discovery/pool/policy/health-gating suite (adapted from AIStudio).
  • RsyncSuggestionTests — hard coverage of parseRsyncSuggestion (injection rejection + clean parse), flag→options mapping, prompt-builder purity, and the graceful no-backend path.

🤖 Generated with Claude Code

Jordan Koch and others added 2 commits August 18, 2026 17:15
Add the shared multi-model LLM load balancer and a natural-language → rsync
assistant to RsyncGUI.

Load balancer (ported pure/network-free pieces from AIStudio verbatim):
- ModelRegistry (DiscoveredModel, Ollama/MLX/frontier discovery, assemblePool),
  LoadBalancer (round-robin / least-busy + health gating), OpenRouterProvider
  (+ OpenAICompatibleRequest), KeychainStore.
- LLMLoadBalancerService mirrors AIStudio's balanced-dispatch wiring: three
  persisted toggles (all local / all frontier / Nova Gateway), health-gated
  failover, least-busy dispatch. Nova is never required — works with zero Nova
  on local (Ollama/MLX) and/or an OpenRouter key. Nova Gateway is one OPTIONAL
  OpenAI-compatible backend (127.0.0.1:18792, health /v1/models); a failed health
  check just marks it unavailable. Settings → AI Assist pane surfaces the toggles,
  endpoints, OpenRouter key (Keychain), and backend status.

Describe-it-in-English rsync:
- Job Editor Basic tab gains an intent field; the balanced LLM proposes a concrete
  rsync command shown for REVIEW and used to pre-fill the builder. Never
  auto-executed — rsync is destructive (--delete), so the user must start the job.
- Pure, network-free RsyncPromptBuilder and a strict output validator
  parseRsyncSuggestion that extracts ONLY a valid rsync invocation and rejects
  everything else: shell chaining (; && |), command substitution (`` $() ),
  redirection (> <), non-rsync programs (rm/sudo/cp), and program-executing rsync
  flags (-e/--rsh/--rsync-path) via a strict flag allow-list.

Tests: network-free LoadBalancerTests (adapted from AIStudio) + RsyncSuggestionTests
covering injection rejection, clean parse, flag→options mapping, prompt-builder
purity, and the graceful no-backend path.

App sandbox remains disabled for the app target (Hardened Runtime/notarization
retained) — required for local backend discovery and process-based MLX inference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Handle LLM replies that wrap the command in a mid-sentence inline code span
(e.g. "Use `rsync -a /a/ /b/`."). Only odd-indexed segments (inside a matched
backtick pair) are considered, so a bare command containing a stray backtick
still falls through to the tokenizer and is rejected as a substitution attempt.
Fixes the CI failure in RsyncSuggestionTests.testExtractsFromInlineBacktickSpan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kochj23
kochj23 merged commit f8b5c35 into main Aug 20, 2026
1 check passed
@kochj23
kochj23 deleted the feature/llm-load-balancer-nl-rsync branch August 20, 2026 14:19
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