Repository navigation
WeaveLLM 0.1.0 — full rewrite (local models, workflow editor, desktop shell) - #30
Conversation
…end shell Boot path (src/main.ts) replaces the legacy gateway entry for WeaveLLM: - electrobun/hutch config: Bun main process, 3 release targets, flatpak - frontend shell: Astro 7 static + Svelte 5 islands (ModelBadge) - src/app/*: config, JSON-log nozzle, Bun.serve proxy, cold-start budget, update check (offline silent, GitHub releases) - src/db/schema.ts: 10 tables, idempotent applySchema, WAL - scripts/build-binaries.ts: hutch per-target build + 100 MB gate - 26 new tests (531 total green), typecheck + lint clean, boot 714 ms
…store - manager.ts: LlamaProcessManager with DI seams (spawn/clock/sleep/health/ log); single spawn --port 0 + deadline-bounded port regex, wait-ready health gate, fail-fast boot with actionable EADDRINUSE, restart with exponential backoff, idle kill, version floor gate, TERM->KILL stop - gguf.ts: yarn_orig_ctx derivation, YaRN scale with ratio>=1 guard - spawn-args.ts: RED-metachar arg validation, YaRN + KV q8_0 + host-only cache-ram args, llama.cpp b9908 floor - model-config.ts: model_config store (upsert, persists across reopen) + snake_case sampler body mapping - retire legacy router-mode closure (routes/middleware/dashboard/config/ orchestrator engine/ui/e2e/playwright) — no longer reachable after the manager rewrite; 163 tests green, typecheck + lint clean - ci: disable legacy e2e job (phase 6 restores); release: disable tag trigger until phase 7 build pipeline
…fallback, /v1 OpenAI proxy, auth
- src/sandbox/runner.ts: unshare -n/sandbox-exec isolation, static policy denial (network/fs/secrets), tmp-only workspace, env whitelist, timeout kill (SIGTERM→SIGKILL), output cap; policy-only fallback when unshare is not permitted (EPERM) - src/providers/embeddings.ts + /v1/embeddings route in src/routes/v1.ts: OpenAI embeddings wire shape over the managed llama-server backend, 404 model_not_found when no local embedder - src/rag/chunks.ts: cosine top-k retrieval over the chunks table (Float32Array BLOBs); empty store and dimension mismatch handled - src/rag/memory.ts: kv_memory conversation store + message injection - src/rag/rag.ts: rag_local embed→retrieve→prompt→answer with explicit no-context notice Tests: +38 (337 total), typecheck and lint clean.
join nodes aggregate their direct predecessors' outputs into a drafts variable; downstream llm_call synthesis consumes every draft as numbered user turns (MoA 3+1). Composed pipeline steps now surface in the outer event stream and results ledger. Adds 6.3 taxonomy-parity, 6.4 MoA+router, and pipeline-composition tests.
parseWorkflowGraph/serializeWorkflowGraph round-trip the full graph (14 node types + every field), tolerating unknown fields for forward compat and naming the offending node/edge on structural failures. validateGraph messages translated to English (editor surfaces them inline); NODE_TYPES registry added for the shared taxonomy.
WorkflowStore persists YAML docs (version bump on resave) and appends execution_history rows. makeApiHandler exposes GET/PUT/DELETE /api/workflows[/:name] plus POST :name/run (injected runner, 404/502 passthrough) and GET :name/logs. PUT validation names the offending node/error via parseWorkflowGraph + validateGraph. Runner contract defined for the boot wiring.
Boot now composes the workflow runtime: WorkflowStore → makeRuntimeServices (registry, sandbox seam, embedder/chunks/memory seams) → makeWorkflowRunner. The /api dispatcher answers /api/workflows; the /v1 handler advertises and runs gateway/<name> models, so legacy chains route through the same path. runCode now goes through the injected sandbox seam instead of importing runSandbox directly. Boot-level integration test covers CRUD, gateway model listing, and a real run over a socket.
Typed websocket protocol for live runs: bind → step_started / step_completed → token (the completed run's OpenAI-wire SSE, then exactly one data: [DONE]) → status. Events are scoped to the socket that bound the workflow; a client disconnect aborts the upstream run via a per-socket AbortController. WorkflowRunner.run gains an optional onEvent telemetry sink threaded into the engine. Plain HTTP /ws answers 426. Integration tests run against a real Bun server with real WebSocket clients, including an abort test; the boot harness exercises /ws end to end.
Add the Svelte 5 editor island on the shell page: palette of the full 14-node taxonomy, SvelteFlow canvas with cycle rejection and live engine validation, and YAML import/export through the workflow-yaml round-trip. All graph logic lives in pure helpers (frontend/src/lib/workflow-nodes.ts) unit-tested with bun test; astro build gates the xyflow interop.
Remove openai-compatible.ts + sdk-surface.ts and their tests; the provider layering now talks to llama-server through the OpenAI wire format directly (http-core + adapters). Drop ai, @ai-sdk/*, express, helmet, @types/express deps; suite drops exactly the deleted tests (422->382 pass, suite stays green).
All 50 tasks complete. Final gate: typecheck + lint clean, bun test 382 pass / 0 fail / 39 files, bun run build + build:frontend green. Tag v0.1.0 remains pending (no tag-capable tool; candidate = this SHA).
…N, spawn-time readiness gate)
… keychain auth, fallback, binding set)
…, localhost-only binding)
…rets, catalog, downloads)
…ing and single-chunk relay
…nomy, deviations)
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
The stderr reader binding in waitForPortAndTail failed typecheck on a fresh install: proc.stderr.getReader() resolves to stream/web's reader while the parameter was typed with bun-types' global ReadableStreamDefaultReader, whose readMany member is absent from the stream/web copy. Type the parameter with the minimal structural surface tailStderr actually consumes instead of the concrete global type, so the build no longer depends on which duplicate wins. Verified on a clean clone: bun run typecheck && bun run lint && bun test (382 pass / 0 fail).
README.md: rewrite to describe WeaveLLM 0.1.0 — real port 4317, WEAVELLM_* env config, DAG workflows (not chains), real endpoint table, Astro 7 desktop shell, current-status note for unwired local backend. AGENTS.md: update architecture map, contracts, and configuration model to match the WeaveLLM source tree; note config.example.yaml as stale. Both verified against src/app/config.ts, src/routes/v1.ts, src/main.ts, and src/app/server.ts.
…onfig, embeddings flag, health models - Add models.active INTEGER NOT NULL DEFAULT 0 with idempotent PRAGMA-gated ALTER migration - Add WEAVELLM_LLAMA_BIN env → llamaBin on AppConfig (default llama) - Add embeddings?: boolean to LlamaSpawnArgsInput → --embeddings flag emission - Add ServerDeps.localModels for /api/health endpoint (optional field) - Change IDLE_TIMEOUT_MS default from 5 min to 10 min (binding Decision 6) - Extract resolveLlamaBin pure function mirroring resolvePort pattern - 15 new tests; 397 total passing (typecheck, lint, test all green) Pre-existing ws.test.ts fixtures updated for the new required AppConfig.llamaBin field (AppConfig contract expansion).
…ess gating, boot wiring Completes tanda 2 (Tasks 4, 5, 6, 8, 9) of the wire-local-backend change: the managed llama-server backend is now wired into the runtime. LocalBackendHub (src/backend/hub.ts): - preflight: version floor b9908, ENOENT-tolerant (per-model error, boot continues) - restoreActive on boot, activate/deactivate (in-flight drain, 1s poll, 30s force) - readiness-gated Provider wrapper: lazy re-spawn after idle-stop, 503 on error, start-latch serializes concurrent spawns; per-request dispatcher via zero-arg localProvider(); embedder with lazy settings designation read - 10-min idle stop, stopAll for graceful shutdown /api/models route group (api.ts): list / status / activate / deactivate behind an optional auth gate; HubError status maps straight to the response. Boot wiring (main.ts): hub.preflight() + restoreActive() before handler construction (binding Decision 2), non-null closures into runtime/v1/health, shared auth gate, BootResult.shutdown drained on SIGINT/SIGTERM. 40 new tests (hub 24, api models 6, boot 3, v1 integration 7); full gate green: 437/437 pass across 41 files.
…deltas into canonical specs - backend-management +6 modified, 2 added; dashboard-api +4 added; local-model-catalog +4 added; embeddings-rag +1 modified, 2 added - move change folder to openspec/changes/archive/2026-09-15-wire-local-backend - add archive-report with final-state facts (19/19 req, 47/47 scenarios, 438 tests, hard-gate PASS, F4 review acknowledged)
- bun start now builds frontend then serves the compiled UI (frontend/dist) with SPA fallback at / and /ui, excluding /api/* and /v1/* - bun run dev runs backend watch + Astro dev in parallel via scripts/dev.ts - static UI handler with traversal protection and WEAVELLM_UI_DIR override - AGENTS.md updated with the new start/dev contract
# Conflicts: # src/app/config.ts # src/app/server.test.ts # src/app/types.ts
…form llama.cpp writes --version to stderr, not stdout; the preflight read only stdout, got empty output, and fail-fast exited(1) silently — the fixed port fix regressed ./start to an immediate silent dead exit. - checkLlamaVersionFloor now accepts the real form '(build 10679, ...)' as well as the condensed bNNNN tag - preflight()/checkVersion() collect stdout+stderr and check their concatenation - hub preflight logs its fatal message through the boot logger so failure is visible, and the hub wires that log at boot Verified: bun run typecheck clean; bun test 458 pass / 42 files; ./start reaches listening 127.0.0.1:4317 in ~200ms cold with the local llama build.
AppImage-style dist/weavellm skeleton: a tiny C stub (scripts/selfextract.c) that reads an appended tar.gz payload trailer at EOF, extracts the Electrobun dev bundle to $XDG_CACHE_HOME/weavellm/<payload sha256> (cached via a .complete marker under a flock, so re-runs skip extraction), defaults WEBKIT_DISABLE_DMABUF_RENDERER=1 (explicit override wins), and execs bin/launcher with exit-code propagation. The trailer byte layout (magic + format version + uint64 LE size + sha256 hex) has ONE source of truth: scripts/selfextract-layout.ts, which also renders the C header the stub includes, so the two sides cannot drift. All pure helpers (cache base, extraction dir, render-env merge, trailer round-trip) are covered by scripts/selfextract-layout.test.ts.
scripts/build-binary.ts orchestrates the portable single-file build: bun run build:frontend -> hutch electrobun build --env=dev (REAL CLI at ~/.hutch/bin/hutch; bunx hutch is the wrong npm package and hutch is not on PATH) -> tar.gz of the flat dev bundle contents -> compile the ~15 KB C stub (generated trailer header from selfextract-layout.ts) -> append payload + trailer to dist/weavellm -> enforce the <100 MB gate (reuses assertBundleSize) and report the payload sha256 + extraction cache path. --skip-frontend / --skip-build reuse an existing SPA / dev bundle. WEAVELLM_ARTIFACTS_DIR overrides the output dir, matching build:binaries. package.json build:binary now points at this pipeline (was the backend-only bun --compile stub). build:binaries doc comment clarifies it targets the Cottontail installer/update flow and is NOT the portable path.
desktop-app-shell now specifies the user-selected distribution form: ONE self-contained executable built from the Electrobun v2 dev bundle via Hutch, under 100 MB, host-only matrix, with no installer / no self-updater. The old Auto-update check requirement (Hutch channel + consent-based install) is replaced by the portable contract with scenarios for single-file run, cached extraction reuse, and the default WEBKIT_DISABLE_DMABUF_RENDERER=1 render environment. The All-three-targets-build scenario is corrected to the host-only reality.
…robun v2 config - main.ts: open Electrobun BrowserWindow targeting the local server when running inside a bundle (direct bun run stays server-only) - app/config: resolveUiDir/uiDirCandidates (env override > dev build > bundled ui) with tests - electrobun.config + test: verified v2 host-only API, bun main process, window opens app's own server - hutch.config: pin electrobun 2.0.2-beta.27 (2.1.0 was never published) - build-binaries: host-only stable channel via real hutch CLI (~/.hutch/bin), Cottontail installer artifacts documented as distinct path - package.json: start -> start-desktop.ts, add electrobun dep - types/electrobun.d.ts: ambient v2 declarations for the throw-on-import stub - tsconfig paths -> ambient declarations; eslint ignores build/artifacts/.hutch - UI: Gentle-AI hybrid theme (global.css, ModelBadge, WorkflowEditor, index) - gitignore: build/, artifacts/, .hutch/, .cottontail-tmp/ - odd: feature docs for desktop-shell-mvp, ui-gentle-ai-theme, portable-binary
Summary
WeaveLLM — full rewrite of the llm-proxy gateway: local llama.cpp models behind an OpenAI-compatible API, workflows composed in a visual editor and exposed as virtual models, a desktop shell, and sandboxed code execution.
/wswebsocket relay, health, models, dashboard APIllm_callmodes, conditional routing), virtual modelgateway/<name>exposure, YAML import/exportunshare -nnetwork isolation, tmp-only FS, timeout-boundedChanges
src/frontend/scripts/openspec/specs/*CHANGELOG.md[0.1.0]release entryTest Plan
bun run typecheck— cleanbun run lint— 0 failbun test— 382 pass / 0 fail / 39 files (961 expect)bun run build— bundles cleanlyContributor Checklist
Co-Authored-BytrailersDelivery follows ordinary repository policy; merge is a human decision.