Commit 21a556b

mo khan <mo@mokhan.ca>
2026-08-30 06:38:52
feat: fetch the default gguf model in bin/setup
A fresh clone had no way to satisfy the gguf provider's default model path -- both lib/elelem/plugins/gguf.rb and the eval harness pointed at ~/models/, a personal path that only existed on the machine the default was chosen on. bin/test would fail immediately on any other checkout. Move the default to ~/.elelem/models/, matching the existing convention for user-level elelem data (mcp.json, prompts/, plugins/, permissions.json all already live under ~/.elelem). Fetch it once from ggml-org's llama.cpp-maintained GGUF mirror in bin/setup, skipping if already present so setup stays safe to re-run, and downloading to a .part file first so an interrupted run can't leave a truncated model that fails to load with a confusing error. This does not make bin/test fully green on a fresh clone -- gpt-oss-20b still fails 5/29 eval cases on model-quality/case-quality grounds (see the last commit's comparison table; search/locate-error-string in particular fails on every model tested, which looks like a case bug). It does make bin/test run to completion instead of erroring out on a missing file, and the failure count is now real, reproducible signal instead of a KeyError.
Changed files (3)
bin
lib
elelem
plugins
spec
bin/setup
@@ -7,3 +7,17 @@ bundle install
 
 # Do any other automated setup that you need to do here
 mise install
+
+# The gguf provider (and its eval suite, run by bin/test) need a model on
+# disk; without one, a fresh clone fails immediately. Fetch the default
+# once, skip if it's already there so setup stays safe to re-run.
+GGUF_MODEL_DIR="$HOME/.elelem/models"
+GGUF_MODEL_URL="https://huggingface.co/ggml-org/gpt-oss-20b-GGUF/resolve/main/gpt-oss-20b-MXFP4.gguf"
+GGUF_MODEL_PATH="$GGUF_MODEL_DIR/gpt-oss-20b-MXFP4.gguf"
+
+if [ ! -f "$GGUF_MODEL_PATH" ]; then
+  mkdir -p "$GGUF_MODEL_DIR"
+  echo "Downloading default gguf model (~12GB) to $GGUF_MODEL_PATH ..."
+  curl -fL --progress-bar -o "$GGUF_MODEL_PATH.part" "$GGUF_MODEL_URL"
+  mv "$GGUF_MODEL_PATH.part" "$GGUF_MODEL_PATH"
+fi
lib/elelem/plugins/gguf.rb
@@ -4,7 +4,7 @@ Elelem::Providers.register(:gguf) do
   gpu = %w[vulkan cuda metal].include?(Elelem::Net::GGUF.backend)
 
   Elelem::Net::GGUF.new(
-    model: ENV.fetch("GGUF_MODEL", File.expand_path("~/models/gpt-oss-20b-MXFP4.gguf")),
+    model: ENV.fetch("GGUF_MODEL", File.expand_path("~/.elelem/models/gpt-oss-20b-MXFP4.gguf")),
     n_ctx: Integer(ENV.fetch("GGUF_N_CTX", "16384")),
     n_threads: Integer(ENV.fetch("GGUF_THREADS", "16")),
     max_tokens: Integer(ENV.fetch("GGUF_MAX_TOKENS", "4096")),
spec/evals/cases_spec.rb
@@ -12,7 +12,7 @@ module Elelem
       end
     end
 
-    def self.gguf(model: ENV.fetch("GGUF_MODEL", File.expand_path("~/models/gpt-oss-20b-MXFP4.gguf")))
+    def self.gguf(model: ENV.fetch("GGUF_MODEL", File.expand_path("~/.elelem/models/gpt-oss-20b-MXFP4.gguf")))
       @gguf ||= begin
         gpu = %w[vulkan cuda metal].include?(Elelem::Net::GGUF.backend)
         Elelem::Net::GGUF.new(