From 45deb6645bbe20b606e14879b321525440b12703 Mon Sep 17 00:00:00 2001 From: infosave2007 Date: Tue, 21 Jul 2026 22:41:45 +0300 Subject: [PATCH] =?UTF-8?q?chore(release):=20v0.2.24=20=E2=80=94=20new=20q?= =?UTF-8?q?uantizations=20(q4t,=20q1t,=20q1s,=20q1p,=20q1)=20and=20feature?= =?UTF-8?q?d=20CMF=20models?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 ++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- src/import.rs | 28 +++++++++++++++++++++- web/js/i18n/en.js | 9 +++++++ web/js/i18n/ru.js | 9 +++++++ web/js/views/import.js | 54 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 108 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00c870a..d5f5a1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,15 @@ All notable changes to this project are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/). +## [0.2.24] - 2026-07-21 + +### Added +- **New Quantization Schemes**: Added support for `Q4_TILED` (q4t), `Q1T` (1.58-bit ternary), `Q1S` (1-bit sparse), `Q1P` (1-bit PTQ error diffusion), and `Q1` (pure binary) in converter backend and web admin UI. +- **Featured Ready-to-Run .cmf Models**: Added 1-tap download cards for pre-converted `.cmf` models (`infosave/Bonsai-1.7Bcmf`, `infosave/Bonsai-8B_2bit_cmf`, `infosave/Bonsai-27Bcmf`). + ## [Unreleased] + ### Added - **Docker Compose** quickstart (`docker-compose.yml` + `config/gateway.compose.toml`): gateway + a local Ollama model in one command. GHCR badge in the READMEs. diff --git a/Cargo.lock b/Cargo.lock index 82ddbef..667bae8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -285,7 +285,7 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "cortiq-gateway" -version = "0.2.15" +version = "0.2.24" dependencies = [ "anyhow", "arc-swap", diff --git a/Cargo.toml b/Cargo.toml index 9f6fde0..9db6454 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cortiq-gateway" -version = "0.2.15" +version = "0.2.24" edition = "2021" rust-version = "1.75" license = "Apache-2.0" diff --git a/src/import.rs b/src/import.rs index 7fbac61..d026072 100644 --- a/src/import.rs +++ b/src/import.rs @@ -260,10 +260,15 @@ fn sanitize(s: &str) -> String { /// Map the UI quant label to the native `cortiq` quant flag. fn map_quant(q: &str) -> &'static str { match q.to_ascii_uppercase().as_str() { + s if s.starts_with("Q4T") || s == "Q4_TILED" => "q4t", s if s.starts_with("Q4") => "q4", - "Q8_2F" => "q8_2f", + "Q8_2F" | "Q82F" => "q8_2f", "F16" | "FP16" => "f16", "VBIT" => "vbit", + "Q1" => "q1", + "Q1P" | "Q1_PTQ" => "q1p", + "Q1S" | "Q1_MASK" => "q1s", + "Q1T" | "Q1_TERNARY" => "q1t", _ => "q8", } } @@ -530,3 +535,24 @@ pub fn start_import(store: Arc, cfg: &CmfCfg, p: ImportParams) -> Resu Ok(ret_id) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_map_quant_variants() { + assert_eq!(map_quant("Q8_2F"), "q8_2f"); + assert_eq!(map_quant("q82f"), "q8_2f"); + assert_eq!(map_quant("Q8_ROW"), "q8"); + assert_eq!(map_quant("Q4_BLOCK"), "q4"); + assert_eq!(map_quant("Q4_TILED"), "q4t"); + assert_eq!(map_quant("q4t"), "q4t"); + assert_eq!(map_quant("vbit"), "vbit"); + assert_eq!(map_quant("q1"), "q1"); + assert_eq!(map_quant("Q1P"), "q1p"); + assert_eq!(map_quant("q1s"), "q1s"); + assert_eq!(map_quant("Q1T"), "q1t"); + assert_eq!(map_quant("F16"), "f16"); + } +} diff --git a/web/js/i18n/en.js b/web/js/i18n/en.js index 7858a2f..dcac5d2 100644 --- a/web/js/i18n/en.js +++ b/web/js/i18n/en.js @@ -384,12 +384,21 @@ export default { "import.state.done": "done", "import.state.error": "error", "import.state.cancelled": "cancelled", + "import.featuredTitle": "Featured Ready-to-Run .cmf Models", + "import.readyCmfBadge": "Ready .cmf", + "import.directDownload": "Download", "import.q.q8_2f": "8-bit two-field (𝒲×θ) — recommended: best quality/size.", "import.q.q8_row": "8-bit per-row — simple, robust.", "import.q.q4": "4-bit block — smallest, lower quality.", + "import.q.q4_tiled": "4-bit tiled (Q4_TILED) — 1.66x faster on Metal GPU.", "import.q.vbit": "Variable 3–8 bit — per-expert bit budgets.", + "import.q.q1t": "1.58-bit ternary (Q1T) — BitNet b1.58 with sparse outliers.", + "import.q.q1s": "1-bit sparse (Q1S) — 1-bit base with holographic outliers.", + "import.q.q1p": "1-bit PTQ (Q1P) — 1-bit with error diffusion.", + "import.q.q1": "1-bit (Q1) — pure binary format.", "import.q.f16": "16-bit — no quantization.", "import.q.f32": "32-bit — full precision (large file).", + "import.adv.title": "Advanced settings", "import.adv.auto": "auto", "import.adv.linearCore": "Linear core", diff --git a/web/js/i18n/ru.js b/web/js/i18n/ru.js index ebb57ce..d8dd9e7 100644 --- a/web/js/i18n/ru.js +++ b/web/js/i18n/ru.js @@ -383,12 +383,21 @@ export default { "import.state.done": "готово", "import.state.error": "ошибка", "import.state.cancelled": "отменено", + "import.featuredTitle": "Рекомендуемые готовые .cmf модели", + "import.readyCmfBadge": "Готовая .cmf", + "import.directDownload": "Скачать", "import.q.q8_2f": "8-бит двухполевой (𝒲×θ) — рекомендуется: лучшее качество/размер.", "import.q.q8_row": "8-бит построчно — просто, надёжно.", "import.q.q4": "4-бит блочно — компактнее, ниже качество.", + "import.q.q4_tiled": "4-бит тайловый (Q4_TILED) — х1.66 быстрее на Metal GPU.", "import.q.vbit": "Переменная 3–8 бит — per-expert бюджеты.", + "import.q.q1t": "1.58-бит тернарный (Q1T) — BitNet b1.58 с оутлайерами.", + "import.q.q1s": "1-бит спарс (Q1S) — 1-бит база с голографическими оутлайерами.", + "import.q.q1p": "1-бит PTQ (Q1P) — 1-бит с переносом ошибки (error diffusion).", + "import.q.q1": "1-бит (Q1) — чистый бинарный формат.", "import.q.f16": "16-бит — без квантования.", "import.q.f32": "32-бит — полная точность (большой файл).", + "import.adv.title": "Расширенные настройки", "import.adv.auto": "авто", "import.adv.linearCore": "Линейное ядро", diff --git a/web/js/views/import.js b/web/js/views/import.js index 97ebba5..654e602 100644 --- a/web/js/views/import.js +++ b/web/js/views/import.js @@ -10,11 +10,23 @@ const QUANTS = [ ["Q8_2F", "import.q.q8_2f"], ["Q8_ROW", "import.q.q8_row"], ["Q4_BLOCK", "import.q.q4"], + ["Q4_TILED", "import.q.q4_tiled"], ["VBIT", "import.q.vbit"], + ["Q1T", "import.q.q1t"], + ["Q1S", "import.q.q1s"], + ["Q1P", "import.q.q1p"], + ["Q1", "import.q.q1"], ["F16", "import.q.f16"], ["F32", "import.q.f32"], ]; +const FEATURED_REPOS = [ + { id: "infosave/Bonsai-1.7Bcmf", label: "Bonsai 1.7B CMF", badge: "1.7B · Ready .cmf" }, + { id: "infosave/Bonsai-8B_2bit_cmf", label: "Bonsai 8B 2-bit CMF", badge: "8B · Ready .cmf" }, + { id: "infosave/Bonsai-27Bcmf", label: "Bonsai 27B CMF", badge: "27B · Ready .cmf" }, +]; + + function field(label, control, hint) { return h("label", { class: "field" }, h("span", {}, label), control, hint ? h("div", { class: "hint" }, hint) : null); @@ -358,6 +370,46 @@ export function renderImport() { ); } + function featuredCard(item) { + const card = h( + "div", + { class: "hf-card featured-card" }, + h("div", { class: "row", style: "align-items:center; justify-content:space-between;" }, + h("div", {}, + h("div", { class: "hf-id" }, item.label), + h("div", { class: "hf-meta" }, + h("span", { class: "badge" }, item.badge), + h("span", { class: "muted" }, item.id) + ) + ), + h("button", { class: "btn primary sm" }, "⬇ " + t("import.directDownload")) + ) + ); + card.querySelector("button").addEventListener("click", async (e) => { + e.stopPropagation(); + try { + await api.startImport({ + repo: item.id, + quant: "Q8_2F", + name: item.id.split("/").pop().toLowerCase(), + }); + toast(t("import.started"), "ok"); + refreshJobs(); + jobsHost.scrollIntoView({ behavior: "smooth", block: "nearest" }); + } catch (err) { + toast(err.message, "err"); + } + }); + return card; + } + + const featuredHost = h( + "div", + { class: "card featured-section" }, + h("div", { class: "card-title" }, "⚡ " + t("import.featuredTitle")), + h("div", { class: "hf-grid" }, ...FEATURED_REPOS.map(featuredCard)) + ); + const root = h( "div", {}, @@ -365,6 +417,7 @@ export function renderImport() { h("h2", {}, t("nav.import")), h("div", { class: "muted" }, t("import.subtitle"))), jobsHost, // progress panel on top, above the search + featuredHost, // featured ready-to-run models h("div", { class: "card" }, searchIn, results), formHost ); @@ -372,3 +425,4 @@ export function renderImport() { refreshJobs(); // resume: show any running/recent conversions immediately return root; } +