Skip to content

Releases: ferrumox/fox

v0.21.0

Choose a tag to compare

@github-actions github-actions released this 09 Aug 10:12

Three changes about being able to tell what the server is doing: a number for the
work its scheduler avoids, a document for what its interface promises, and a
model label so its metrics say which model an observation belongs to. The last
one renames every metric, which is why this is a minor rather than a patch.

And then a fourth, found by pointing a client at a server with real models on it:
the model listing endpoints were unusable on any machine with more than a few
gigabytes of GGUFs. Fixing that changes what digest means, which is Tier 1, so
it lands here rather than in a patch.

Changed

  • BREAKING (Tier 2): the Prometheus metrics move from ferrumox_* to fox_*.
    The binary, the CLI, the docs and everything a user types say fox; the metrics
    endpoint was the only place that said ferrumox. Renaming breaks every existing
    dashboard, which is exactly why it happens now: after 1.0 the prefix is frozen and the
    inconsistency would be permanent.

    Migration: s/ferrumox_/fox_/ in dashboards, alerts and recording rules. All thirteen
    names change prefix only — type and meaning are identical.

    Per COMPATIBILITY.md this is Tier 2: observable, changed on a
    minor bump with a CHANGELOG entry and no promised deprecation window.

  • Every metric now carries a model label. Fox serves several models at once with
    --max-models, and until now nothing on /metrics said which one was responsible: a
    saturated KV cache, a deep queue and a bad p99 all looked like properties of the server
    rather than of one model inside it.

    The label could not be added without a cap. Model names are whatever the client asks
    for — fox pull accepts arbitrary HuggingFace repos — so the label set is influenced
    from outside the server, and an unbounded one turns /metrics into a memory leak that
    every scrape then has to serialise. The cap is 32 distinct values per process; past that
    everything collapses into model="<other>", with a warning emitted once per process.
    Serving is never degraded by this.

    The cap counts models ever seen, not loaded at once: a load/evict/load cycle reuses
    its slot instead of consuming a new one, so nobody can walk the limit upward by churning
    models.

    Evicting a model retires its series. Counters could have been left alone — a monotonic
    total that stops advancing is still true — but the gauges could not:
    fox_kv_cache_usage_ratio for an evicted model would sit at its last value forever, and
    a dashboard would go on reporting a full KV cache for a model that no longer holds a
    single block.

    scripts/e2e_smoke.py read two of these metrics by line prefix and now sums the series
    instead of keeping the last, which with more than one model loaded would have reported a
    single model's drafting.

    A side effect of labelling, checked against a real server: a metric no longer appears
    on /metrics until its first observation.
    Previously, unlabelled, all thirteen were
    registered at startup and always emitted at zero. Now a series exists once something
    touches it, so a freshly started server shows only the three gauges the engine loop
    refreshes, and fox_requests_total does not appear until the first request finishes.
    This is normal Prometheus behaviour for labelled metrics, but a panel that assumed
    "always present, possibly zero" now gets an empty result: use or vector(0) in those
    queries.

  • BREAKING (Tier 1): the digest in /api/tags, /api/ps and /api/show is derived
    from the model file's name, size and mtime, not from its contents.
    It is still
    sha256:<hex> and still changes whenever the file is replaced, but it is now an opaque
    identifier rather than a content hash.

    This is the fix for the hang below, not a cosmetic change: a digest that is a content
    hash cannot be produced without reading every byte of the file, and there is nowhere on
    a listing request to put that work. Ollama can report a real hash because its blobs are
    content-addressed and hashed once at pull; fox stores plain GGUF files in a directory
    that users also drop models into by hand.

    Nothing in fox resolves a model by digest — it identifies, it does not address — and
    /api/pull already emitted sha256:<filename> rather than a content hash, so no fox
    client could have been verifying one. Per COMPATIBILITY.md,
    changing a field's meaning is Tier 1 and belongs on a minor bump with an entry saying
    so. This is that entry.

Fixed

  • GET /api/tags no longer hangs with a core pinned at 100%. It computed the SHA-256
    of every .gguf in the models directory before it could answer: measured on a 27 GB
    directory, 51.6 s for the first call. /api/ps and /api/show did the same.

    What turned a slow endpoint into an apparently dead server is that the digest cache was
    only written once a hash finished, and identical work in flight was never shared. Each
    retry — a curl re-run, an Open WebUI refresh, a page reload — started a full re-hash of
    the whole directory on another blocking thread. Retrying, the natural response to no
    response, is what saturated the CPU. GET / stayed instant throughout, because it
    touches no disk, which made the server look up rather than stuck.

    The same directory now answers in 22 ms, and eight concurrent requests complete in 30 ms
    total. /api/ps additionally re-read the models directory once per resident model; it
    now reads it once.

  • GET /health no longer loads a model to answer. It called get_or_load, so the
    liveness probe blocked for as long as a multi-gigabyte load took — curl -m 3 against
    a server whose model was not resident simply timed out — and it could cause a load,
    which under the default --max-models 1 evicts whatever is serving traffic. An
    orchestrator polling /health during startup gets a timeout and restarts the process
    before it ever finishes loading: the probe becomes the outage. Found while verifying an
    unrelated fix on a server holding a 10 GB model.

    It now reports residency instead of establishing it, answering in 6 ms, and does not
    count as a use — a probe that refreshed the LRU would keep a model resident forever and
    --keep-alive-secs would never fire. The response gains model_loaded, because
    otherwise "not loaded yet" and "loaded and idle" are the same body; that state was
    nearly unreachable while the handler loaded on demand and is now the normal one before
    the first request.

  • Diffusion models are refused at load instead of served as gibberish. LLaDA, Dream,
    RND1 and the rest do not generate left to right — they unmask a sequence over a fixed
    number of steps, which is why llama.cpp ships a separate diffusion tool for them.
    fox's decode loop is autoregressive, and loading one anyway did not fail: it produced
    replies with mask tokens (<|mask_start|>) embedded in them, fragments out of order,
    duplicated spans and truncation. Reported as an output-formatting bug, which is exactly
    what it looks like from the client side. llama_model_is_diffusion() is now checked
    after load and the model is rejected with an explanation.

  • An unrecognised --type-kv or --split-mode value is an error, not a shrug. Both
    parsers answered anything they did not recognise with the default and no message, so
    type_kv = "turbo3" in config.toml quantised nothing, said nothing, and left the
    operator believing the setting had applied. A config file has no completion and no type
    checking; this warning was the only feedback available and it was not being given.
    Rejected at startup now, before anything loads, naming the accepted values.

  • A missing GPU dependency no longer stops fox compiling at all. build.rs enabled
    the Vulkan backend on the strength of any single signal — VULKAN_SDK set, or glslc
    on PATH, or vulkan.h present. ggml-vulkan then opens with
    find_package(Vulkan COMPONENTS glslc REQUIRED) and find_package(SPIRV-Headers CONFIG REQUIRED), and a missing one of those is a fatal CMake error rather than a fallback.
    So detecting half a toolchain did not produce a CPU build, it failed the whole cargo
    build — and with no way to turn Vulkan off, the user could not build fox at all. Reported
    from Windows with a LunarG SDK 1.3.246, which ships glslc and the loader but no
    SPIRV-HeadersConfig.cmake.

    All three pieces are now checked before the backend is switched on, on Linux and
    Windows alike, and a partial toolchain produces a CPU build plus a warning naming what
    is missing and how to install it. FOX_NO_VULKAN=1 forces it off, FOX_FORCE_VULKAN=1
    forces it on — the second doubling as the way to re-run the check, since installing a
    package does not invalidate a build script.

    GGML_VULKAN=OFF is now passed explicitly instead of being left unset. CMake caches the
    switch in target/, so once any build had configured with it ON, every later build in
    that tree inherited ON regardless — someone who hit this and then fixed their toolchain
    would have kept failing, with cargo clean and a full llama.cpp rebuild as the apparent
    only cure.

  • Releases are published with notes. softprops/action-gh-release was only ever
    handed files, so every release page was a bare list of six assets and nothing else.
    Two of those assets are tarballs differing by a -vulkan suffix, which meant the page
    never said that a GPU build existed or which file to take — and the one without a
    suffix reads as the default. It cost someone a bug report: libggml-vulkan.so looked
    missing from the release, when it was in the other tarball all along.

    The body is now this version's CHANGELOG section (scripts/release_notes.py) plus a
    table of which download is which, how to verify it, and the note that fox probe
    reports the backend actually in use. Written by a job that runs after both ...

Read more

v0.20.5

Choose a tag to compare

@github-actions github-actions released this 08 Aug 09:36
release: v0.20.5

v0.20.4

Choose a tag to compare

@github-actions github-actions released this 04 Aug 09:23
release: v0.20.4

v0.20.3

Choose a tag to compare

@github-actions github-actions released this 04 Aug 08:01
release: v0.20.3

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 21 Jul 06:34
release: v0.13.0

fox becomes a real server under concurrent, long-prompt, long-conversation load. The
three serving-robustness gaps from the 0.12 capabilities checklist are closed
(docs/design/serving-robustness.md): chunked prefill breaks a long prompt into
per-step chunks so it interleaves with other requests' generation instead of
head-of-line-blocking the engine loop; context rolling discards the oldest KV
window when a conversation fills n_ctx so generation continues instead of stopping
with length; and the Jinja chat template is compiled once and cached instead of
re-parsed on every request. A new fox bench-prefill quantifies the chunked-prefill
win, and every change rides the 0.12 regression net (golden tests in CI + the
scheduler conservation stress test).

Squash-merge of develop @ 37db0f4 into main (Git Flow release). First parent:
v0.12.0 (keeps main's release line); second parent omitted — this history was
rewritten to one clean commit per release (see git-history-squash, 2026-07-06).

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 08 Jul 14:55

[0.12.0] - 2026-07-06

GPU inference becomes a first-class, reproducible path, and the model-architecture
rework (started in 0.11) is finished off. Vulkan is validated end-to-end on an AMD
Radeon 890M (gfx1150, RDNA 3.5) and shipped three ways — a Docker image, a prebuilt
release tarball, and a make vulkan bundle — with fox now reporting the active
backend at startup. On the correctness side, P4 (API consistency) lands, and the
rework's regression net is wired up for real: golden tests run in CI against a live
model, and a stress test settles the last open question (§7) by proving the prefix
cache doesn't leak.

Added

  • Dockerfile.vulkan — a reproducible Vulkan build for AMD/Intel iGPUs and any
    Vulkan-capable GPU (no CUDA/ROCm). Validated end-to-end on an AMD Radeon 890M
    (gfx1150, RDNA 3.5): coherent output, GPU-accelerated, both by extracting the
    binary to run natively and by running the image with --device /dev/dri. The image
    ships the Mesa Vulkan driver and falls back to CPU when no GPU is present.
    CONTRIBUTING documents the GPU-build story, including the exact toolchain
    (glslc, glslang-tools, libvulkan-dev, spirv-headers) and the
    build-in-container / run-on-host split.
  • fox reports the active compute backend at startup. fox run, fox serve (in
    the log) and fox probe now show whether inference runs on the GPU (e.g.
    Vulkan0 — AMD Radeon 890M) or the CPU, read from the ggml device registry —
    closing the "is it actually using my GPU?" gap. Exposed on ModelInfo.backend.
  • Prebuilt Vulkan binary in releasesrelease.yml now builds a
    x86_64-unknown-linux-gnu-vulkan tarball (on Ubuntu 24.04) alongside the CPU one,
    so GPU users get a ready-to-run binary. The Vulkan tarball needs glibc 2.39+ and a
    Vulkan driver (Mesa RADV/ANV, etc.) at runtime.
  • make vulkan — builds the Dockerfile.vulkan image and extracts the bundle
    (fox, fox-bench, libggml-vulkan.so) to ./fox-vulkan/, so you get a
    GPU-enabled binary that runs natively on any host with a Vulkan driver — no build
    toolchain needed on the host.
  • Golden tests now run in CI — a new golden job builds llama.cpp for real (the
    only CI job that does; the rest stay on the fast stub) and runs the golden suite
    against a tiny GGUF (Qwen2.5-0.5B) on CPU: ModelInfo invariants, non-degenerate
    embeddings, and tokenize round-trips on emoji/CJK. The model and the llama.cpp build
    are cached so it only pays the full cost when either changes. This wires up the
    regression net that P0 built but only ran locally.
  • Prefix-cache leak stress test (scheduler::tests::stress_prefix_cache_no_leak) —
    settles the last open question of the model-architecture rework (§7). It drives 400
    admit/finish/cache/hit/refuse-when-full cycles and asserts, after every step, that
    every seq_id and KV block is owned by exactly one of {pool, running request, cache
    entry} — never dropped, never duplicated — and that allocation returns to zero after
    draining. Confirms the prefix cache does not leak (the initial automated flag was
    a false positive). Adds KVCacheManager::allocated_blocks() for the assertion.

Changed

  • Sampling defaults centralized (src/api/shared/sampling_defaults.rs) — the
    per-request defaults were duplicated as magic literals across the OpenAI and Ollama
    handlers. They now live in one table keyed by API surface, with the cross-surface
    divergence documented as a deliberate decision: the OpenAI surface (/v1/*)
    mirrors OpenAI (no top_k, no repeat penalty) while the Ollama surface (/api/*)
    mirrors upstream Ollama (top_k = 40, repeat_penalty = 1.1). A unit test locks
    the divergence so it can't be "unified" by accident. (Model-architecture rework P4.)

Fixed

  • API docs listed the wrong sampling defaults. docs/api/{openai,ollama}.md
    claimed temperature = 1.0 / top_p = 1.0 (actual: 0.8 / 0.9) and the Ollama
    page showed top_k = 0 / repeat_penalty = 1.0 when fox actually applies Ollama's
    40 / 1.1. Corrected, with a note explaining the deliberate /v1 vs /api
    divergence.
  • --max-models help now states the default-1 trade-off — a request for a second
    model evicts the first (logged), which is the safe choice for small-VRAM iGPUs;
    raise it if you have the VRAM.

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 03 Jul 07:49

[0.11.0] - 2026-07-03

Model-architecture correctness rework (see
docs/design/model-architecture-rework.md) — makes per-model facts a single
inspectable source of truth and closes several "fix one model, break another" gaps.

Added

  • fox probe <model> — loads a model and prints its resolved ModelInfo
    (architecture, n_embd, head counts, head_dim, layers, trained context, EOS,
    embedded-template presence, native-thinking/seq-copy, recommended sampling), then
    flags contradictions between the model's metadata and the formulas fox uses.
    Unlike fox show (which guesses from the filename), probe reads the truth.
  • ModelInfo — one inspectable snapshot of a loaded model's facts, the basis of
    the rework and of fox probe.
  • Golden regression tests (make golden GOLDEN_MODEL=<path.gguf>) — real-model
    assertions (ModelInfo invariants, non-degenerate embeddings, tokenize round-trip)
    that lock in the fixes below. Gated to real builds; the stub CI is unaffected.
  • Community-health filesCODE_OF_CONDUCT.md, issue templates (bug/feature)
    and a pull-request template. Cargo.toml gains package metadata (repository,
    homepage, documentation, keywords, categories).

Changed

  • CI/CD workflows simplified to stop failing. The Docker and Release workflows
    dropped their fragile multi-platform matrices (arm64/CUDA, ROCm apt bundle,
    aarch64 cross, Windows+Vulkan, macOS) — which failed often — for a single reliable
    linux/amd64 build; Docker now builds+pushes directly (no push-by-digest/manifest
    merge). The redundant test-linux-build workflow (only ran on -test tags, pinned
    rotting ROCm versions) was removed. GPU users can use the Docker image or build
    locally; platforms will be re-added once each is verified in isolation.

  • Chat prompts now execute the model's real Jinja template (via minijinja +
    minijinja-contrib pycompat) instead of llama.cpp's simplified built-in format,
    and tokenize the result with the template's own BOS (add_special=false, no
    double BOS) and real control tokens (parse_special=true, not literal text). The
    prompt now matches what each model was trained on. Falls back to the built-in
    format when a model has no embedded template or it fails to render.

  • Thinking/reasoning is now opt-in and correctly detected. supports_thinking
    recognizes models whose chat template exposes an enable_thinking toggle
    (Gemma-4, Qwen3), not just the <think>-token heuristic — fox probe now reports
    Native thinking: yes for Gemma. Thinking activates only when the request opts in
    (OpenAI: a think: true extension field; Ollama: the existing think field) and
    threads enable_thinking into the model's Jinja template; the default is off (no
    reasoning latency unless asked). Note: clean separation of reasoning from the
    answer works for both <think>-delimited models (Qwen3, DeepSeek-R1) and
    channel-format models (Gemma's <|channel>/<channel|>): the output filter AND
    the API-layer thinking extraction read each model's reasoning markers via
    Model::reasoning_delimiters, detected from the model's OWN chat template through
    a small documented format registry (REASONING_FORMATS) — never the model name.
    Supporting a new reasoning format is one registry line plus a golden test.
    Tool-calling through the template remains a follow-up.

Fixed

  • Embeddings returned an all-zeros vector for every model. The generation
    context uses pooling_type = NONE, so llama_get_embeddings_seq returned NULL and
    fox served a zero vector. /v1/embeddings and /api/embed now mean-pool the
    per-token embeddings (llama_get_embeddings_ith) and L2-normalize the result.
  • n_embd was reconstructed as num_heads * head_dim, wrong for Gemma/MLA-class
    models (head_dim != n_embd/n_head). This produced wrong-length embeddings and an
    out-of-bounds read of the embedding buffer. n_embd is now read from
    llama_model_n_embd and stored on ModelConfig.
  • The KV block pool was sized from an independent formula that could disagree with
    the backend's real n_ctx (and is wrong for shared/SWA KV, MLA and recurrent
    models), letting fox over-claim KV and crash llama_decode under load. The serving
    paths now size the pool from llama_n_ctx so it follows the backend exactly.
  • frequency_penalty / presence_penalty were accepted but silently ignored.
    They are now applied in the sampler with OpenAI semantics
    (logit -= presence*(seen) + frequency*count), threaded from the request. Default
    0.0 (disabled).
  • Misleading load-failure message. diagnose_load_failure asserted "not enough
    memory" on any failure (its condition was almost always true), even when the real
    cause was a missing compute backend. It now claims OOM only when free memory is
    actually below the model size, and otherwise lists the real possible causes.
  • Image/audio content is no longer dropped silently. The OpenAI handler now
    warns when a request carries non-text content blocks (fox has no vision/audio
    support). --swap-fraction is documented as reserved/not-yet-implemented rather
    than appearing to do something.

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 01 Jul 20:21

[0.10.0] - 2026-06-30

Re-baselines the project after retracting a premature 1.0.0. The version line continues at 0.10.x and will reach 1.0.0 only when the engine is proven stable. This release also migrates the vendored llama.cpp to upstream and removes TurboQuant.

Changed

  • Vendored llama.cpp now tracks upstream (ggml-org/llama.cpp @ b9842). Previously the
    submodule pointed at a long-lived fork that carried the TurboQuant patches and had drifted
    ~1,200 commits / 3 months behind upstream. Tracking upstream restores binding/library version
    parity, lets a clean --recurse-submodules clone fetch the pinned commit, and unblocks newer
    model architectures (e.g. Gemma 4) without maintaining a fork. The migration required only a
    build-flag fix in build.rs (LLAMA_BUILD_APP/UI=OFF) — zero FFI changes.

Removed

  • TurboQuant KV cache quantization (turbo2 / turbo3 / turbo4). The fork's custom GGML
    type IDs collided with upstream's (e.g. Q1_0=41 vs TURBO3_0=41), making it impossible to
    follow upstream while keeping TurboQuant. Compatibility with upstream llama.cpp was prioritized.
    KV cache quantization remains available via the standard llama.cpp types f16, q8_0 and
    q4_0. Configs or commands referencing turbo* KV types must switch to one of these.

v0.9.0

Choose a tag to compare

@ManuelSLemos ManuelSLemos released this 15 Mar 19:22

[0.9.0] - 2026-03-15

Added

  • Multi-Model supportModelRegistry loads and serves multiple models simultaneously
    with LRU eviction.

    • New src/model_registry.rs: ModelRegistry, EngineEntry, RegistryConfig.
    • GET /api/ps now lists all currently-loaded models (previously only the one model).
    • GET /v1/models now lists all .gguf files in models_dir (not just the loaded one).
    • Each inference/embedding request is routed to the correct engine based on the model field;
      unknown models return HTTP 404.
    • DELETE /api/delete now also unloads the model from the registry if it was loaded.
  • --max-models flag (FOX_MAX_MODELS env var, default 1) — maximum number of models
    kept in memory simultaneously; excess models are evicted LRU-first.

  • --alias-file flag (FOX_ALIAS_FILE env var) — optional TOML file mapping short names
    to model stems (e.g. "llama3" = "Llama-3.2-3B-Instruct-f16").
    Default path: ~/.config/ferrumox/aliases.toml.

Changed

  • AppState replaces engine: Arc<InferenceEngine> with registry: Arc<ModelRegistry> +
    primary_model: String. Backward-compatible: fox serve --model-path X.gguf works unchanged.
  • router() signature updated accordingly.
  • Engine run-loop is now started inside ModelRegistry::get_or_load and aborted automatically
    on LRU eviction via Drop on EngineEntry.

[0.8.0] - 2026-03-15

Added

  • Embeddings API — unlocks RAG pipelines (LangChain, LlamaIndex, Open WebUI RAG, etc.)

    • POST /v1/embeddings — OpenAI-compatible endpoint; accepts input as a string or array
      of strings, returns data[].embedding vectors.
    • POST /api/embed — Ollama-compatible endpoint; returns embeddings: [[f32]].
    • InferenceEngine::embed() async method; Model::get_embeddings() + Model::embedding_dim()
      trait methods with full LlamaCppModel implementation via llama_set_embeddings /
      llama_get_embeddings_seq FFI and stub fallback.
    • New types: EmbeddingInput (untagged enum for String/Vec), EmbeddingRequest,
      EmbeddingObject, EmbeddingUsage, EmbeddingResponse, OllamaEmbedRequest,
      OllamaEmbedResponse.
  • POST /api/pull with SSE streaming — download models from HuggingFace Hub via the
    server API, identical to Ollama's pull flow.

    • Emits newline-delimited JSON events: pulling manifestdownloading (with digest,
      total, completed bytes) → verifying sha256 digestsuccess.
    • Automatically selects Q4_K_M quantization when available, otherwise picks the first GGUF.
    • New --hf-token flag on fox serve (also HF_TOKEN env var) forwarded to pulls.
    • New AppState.hf_token field; new file src/api/pull_handler.rs.
    • New types: PullRequest, PullStatus.
  • Release binaries + install.sh — one-command installation.

    • .github/workflows/release.yml — triggered on v* tags; builds for four targets:
      x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-apple-darwin,
      aarch64-apple-darwin. Uploads tarballs as GitHub Release assets.
    • install.sh — detects OS + arch, downloads the correct tarball, installs to
      /usr/local/bin/fox (configurable via --prefix).
    • fox.service — systemd unit for running fox serve as a daemon.

Changed

  • Cargo.toml: version bumped to 0.8.0.
  • src/api/routes.rs: router() now takes an extra hf_token: Option<String> parameter.
  • src/cli/serve.rs: ServeArgs gains --hf-token / HF_TOKEN.

[0.7.0] - 2026-03-14

Added

  • Ollama-compatible API layer (src/api/routes.rs, src/api/types.rs)

    • GET /api/tags — lists all .gguf models in ~/.cache/ferrumox/models/ with name,
      size, SHA256 digest, architecture family, quantization level, and modified_at timestamp.
      Open WebUI and Continue.dev use this endpoint to discover available models.
    • GET /api/ps — returns the currently loaded model with real file size (bytes) and
      SHA256 digest looked up from disk.
    • POST /api/show — returns detailed metadata for a named model: architecture family,
      quantization, human-readable size, digest, modification date, and file path.
    • DELETE /api/delete — removes a .gguf file from the models directory by model name
      or filename. Returns 404 if the model is not found.
    • New response types: OllamaModel, OllamaDetails, TagsResponse, PsEntry,
      PsResponse, ShowRequest, ShowResponse, DeleteRequest.
    • SHA256 digest computed once per file via sha2 + hex and cached in AppState
      (Arc<Mutex<HashMap<PathBuf, String>>>). Subsequent requests for the same file return
      instantly.
    • New dependencies: sha2 = "0.10", hex = "0.4".
  • models_dir added to AppState (src/api/routes.rs, src/cli/serve.rs)

    • router() now accepts a models_dir: PathBuf parameter (default:
      ~/.cache/ferrumox/models) used by the Ollama-compat handlers.
    • src/cli/show::parse_architecture and parse_quantization promoted to pub(crate)
      so they can be reused by the API layer without duplication.

Compatibility

With v0.7.0, Open WebUI and Continue.dev work out of the box by pointing their
Ollama URL to http://localhost:8080. No other configuration change is required.

v0.6.0

Choose a tag to compare

@ManuelSLemos ManuelSLemos released this 14 Mar 19:53

[0.6.0] - 2026-03-13

Added

  • CLI visual overhaul — minimalista con color (src/cli/theme.rs, all CLI modules)

    • New src/cli/theme.rs module centralises all ANSI styling. Respects NO_COLOR and
      non-TTY contexts (pipes, CI) — every helper silently falls back to plain text.
    • New direct dependency: crossterm = "0.28".
    • fox run loading spinner — replaces the static "Loading model… done." line with a
      cyan Braille spinner (indicatif) that clears itself and prints ✓ Model loaded.
      (bold green) on success.
    • REPL banner — after load, prints 🦊 <model name> (bold white), a dim separator
      and a dim hint line (/bye o Ctrl+D para salir · N tokens).
    • Prompt glyph (bold cyan) replaces "You: ".
    • Thinking spinner — a dim Braille spinner labelled "Thinking…" runs while the model
      generates; cleared on the first emitted token.
    • Role label Fox (bold yellow) is printed once to stderr immediately before the
      first token, producing Fox <streamed response> inline.
    • Per-turn timing — dim N tokens · X.Xs line printed after each assistant turn.
    • fox list — table header bold, separator dim, SIZE column blue, MODIFIED dim.
    • fox ps — table header bold, separator dim; STATUS ok → bold green; KV cache
      usage colour-coded (green < 50 %, yellow < 80 %, red ≥ 80 %).
    • fox show — all key/value rows use theme::print_kv_pair (key bold+dim, padded).
    • fox pull — post-download success line uses ✓ Saved to … (bold green); hint
      lines for fox run / fox serve are dimmed.
    • fox serve — prints 🦊 <model> · listening on <addr> (green) to stderr when
      the server is ready.
  • Interactive REPL mode for fox run (src/cli/run.rs)

    • Running fox run --model-path model.gguf without a prompt now opens a conversational chat session.
    • Full message history is maintained across turns: each new turn sends the complete history through apply_chat_template, giving the model proper context.
    • Exit commands: /bye, /exit, exit, quit, or Ctrl+D (EOF).
    • Existing one-shot behavior (fox run --model-path model.gguf "prompt") is fully preserved.
    • The engine loop stays alive across turns; no model reload between messages.

Changed

  • Project renamed from ferrum-engine to ferrumox — the CLI binary is now fox, the benchmark binary is fox-bench.
    • All environment variables renamed from FERRUM_* to FOX_* (e.g. FOX_MODEL_PATH, FOX_PORT).
    • Model cache directory changed from ~/.cache/ferrum/models to ~/.cache/ferrumox/models.
    • Prometheus metric names updated from ferrum_* to ferrumox_*.
    • Build stub flag renamed from FERRUM_SKIP_LLAMA to FOX_SKIP_LLAMA.
    • Docker image tag changed from ferrum-engine:latest to ferrumox:latest.