Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45deb6645b |
@@ -3,8 +3,15 @@
|
|||||||
All notable changes to this project are documented here.
|
All notable changes to this project are documented here.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
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]
|
## [Unreleased]
|
||||||
|
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- **Docker Compose** quickstart (`docker-compose.yml` + `config/gateway.compose.toml`):
|
- **Docker Compose** quickstart (`docker-compose.yml` + `config/gateway.compose.toml`):
|
||||||
gateway + a local Ollama model in one command. GHCR badge in the READMEs.
|
gateway + a local Ollama model in one command. GHCR badge in the READMEs.
|
||||||
|
|||||||
Generated
+1
-1
@@ -285,7 +285,7 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortiq-gateway"
|
name = "cortiq-gateway"
|
||||||
version = "0.2.15"
|
version = "0.2.24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cortiq-gateway"
|
name = "cortiq-gateway"
|
||||||
version = "0.2.15"
|
version = "0.2.24"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.75"
|
rust-version = "1.75"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|||||||
+27
-1
@@ -260,10 +260,15 @@ fn sanitize(s: &str) -> String {
|
|||||||
/// Map the UI quant label to the native `cortiq` quant flag.
|
/// Map the UI quant label to the native `cortiq` quant flag.
|
||||||
fn map_quant(q: &str) -> &'static str {
|
fn map_quant(q: &str) -> &'static str {
|
||||||
match q.to_ascii_uppercase().as_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",
|
s if s.starts_with("Q4") => "q4",
|
||||||
"Q8_2F" => "q8_2f",
|
"Q8_2F" | "Q82F" => "q8_2f",
|
||||||
"F16" | "FP16" => "f16",
|
"F16" | "FP16" => "f16",
|
||||||
"VBIT" => "vbit",
|
"VBIT" => "vbit",
|
||||||
|
"Q1" => "q1",
|
||||||
|
"Q1P" | "Q1_PTQ" => "q1p",
|
||||||
|
"Q1S" | "Q1_MASK" => "q1s",
|
||||||
|
"Q1T" | "Q1_TERNARY" => "q1t",
|
||||||
_ => "q8",
|
_ => "q8",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -530,3 +535,24 @@ pub fn start_import(store: Arc<JobStore>, cfg: &CmfCfg, p: ImportParams) -> Resu
|
|||||||
|
|
||||||
Ok(ret_id)
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+9
@@ -384,12 +384,21 @@ export default {
|
|||||||
"import.state.done": "done",
|
"import.state.done": "done",
|
||||||
"import.state.error": "error",
|
"import.state.error": "error",
|
||||||
"import.state.cancelled": "cancelled",
|
"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_2f": "8-bit two-field (𝒲×θ) — recommended: best quality/size.",
|
||||||
"import.q.q8_row": "8-bit per-row — simple, robust.",
|
"import.q.q8_row": "8-bit per-row — simple, robust.",
|
||||||
"import.q.q4": "4-bit block — smallest, lower quality.",
|
"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.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.f16": "16-bit — no quantization.",
|
||||||
"import.q.f32": "32-bit — full precision (large file).",
|
"import.q.f32": "32-bit — full precision (large file).",
|
||||||
|
|
||||||
"import.adv.title": "Advanced settings",
|
"import.adv.title": "Advanced settings",
|
||||||
"import.adv.auto": "auto",
|
"import.adv.auto": "auto",
|
||||||
"import.adv.linearCore": "Linear core",
|
"import.adv.linearCore": "Linear core",
|
||||||
|
|||||||
Vendored
+9
@@ -383,12 +383,21 @@ export default {
|
|||||||
"import.state.done": "готово",
|
"import.state.done": "готово",
|
||||||
"import.state.error": "ошибка",
|
"import.state.error": "ошибка",
|
||||||
"import.state.cancelled": "отменено",
|
"import.state.cancelled": "отменено",
|
||||||
|
"import.featuredTitle": "Рекомендуемые готовые .cmf модели",
|
||||||
|
"import.readyCmfBadge": "Готовая .cmf",
|
||||||
|
"import.directDownload": "Скачать",
|
||||||
"import.q.q8_2f": "8-бит двухполевой (𝒲×θ) — рекомендуется: лучшее качество/размер.",
|
"import.q.q8_2f": "8-бит двухполевой (𝒲×θ) — рекомендуется: лучшее качество/размер.",
|
||||||
"import.q.q8_row": "8-бит построчно — просто, надёжно.",
|
"import.q.q8_row": "8-бит построчно — просто, надёжно.",
|
||||||
"import.q.q4": "4-бит блочно — компактнее, ниже качество.",
|
"import.q.q4": "4-бит блочно — компактнее, ниже качество.",
|
||||||
|
"import.q.q4_tiled": "4-бит тайловый (Q4_TILED) — х1.66 быстрее на Metal GPU.",
|
||||||
"import.q.vbit": "Переменная 3–8 бит — per-expert бюджеты.",
|
"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.f16": "16-бит — без квантования.",
|
||||||
"import.q.f32": "32-бит — полная точность (большой файл).",
|
"import.q.f32": "32-бит — полная точность (большой файл).",
|
||||||
|
|
||||||
"import.adv.title": "Расширенные настройки",
|
"import.adv.title": "Расширенные настройки",
|
||||||
"import.adv.auto": "авто",
|
"import.adv.auto": "авто",
|
||||||
"import.adv.linearCore": "Линейное ядро",
|
"import.adv.linearCore": "Линейное ядро",
|
||||||
|
|||||||
Vendored
+54
@@ -10,11 +10,23 @@ const QUANTS = [
|
|||||||
["Q8_2F", "import.q.q8_2f"],
|
["Q8_2F", "import.q.q8_2f"],
|
||||||
["Q8_ROW", "import.q.q8_row"],
|
["Q8_ROW", "import.q.q8_row"],
|
||||||
["Q4_BLOCK", "import.q.q4"],
|
["Q4_BLOCK", "import.q.q4"],
|
||||||
|
["Q4_TILED", "import.q.q4_tiled"],
|
||||||
["VBIT", "import.q.vbit"],
|
["VBIT", "import.q.vbit"],
|
||||||
|
["Q1T", "import.q.q1t"],
|
||||||
|
["Q1S", "import.q.q1s"],
|
||||||
|
["Q1P", "import.q.q1p"],
|
||||||
|
["Q1", "import.q.q1"],
|
||||||
["F16", "import.q.f16"],
|
["F16", "import.q.f16"],
|
||||||
["F32", "import.q.f32"],
|
["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) {
|
function field(label, control, hint) {
|
||||||
return h("label", { class: "field" }, h("span", {}, label), control,
|
return h("label", { class: "field" }, h("span", {}, label), control,
|
||||||
hint ? h("div", { class: "hint" }, hint) : null);
|
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(
|
const root = h(
|
||||||
"div",
|
"div",
|
||||||
{},
|
{},
|
||||||
@@ -365,6 +417,7 @@ export function renderImport() {
|
|||||||
h("h2", {}, t("nav.import")),
|
h("h2", {}, t("nav.import")),
|
||||||
h("div", { class: "muted" }, t("import.subtitle"))),
|
h("div", { class: "muted" }, t("import.subtitle"))),
|
||||||
jobsHost, // progress panel on top, above the search
|
jobsHost, // progress panel on top, above the search
|
||||||
|
featuredHost, // featured ready-to-run models
|
||||||
h("div", { class: "card" }, searchIn, results),
|
h("div", { class: "card" }, searchIn, results),
|
||||||
formHost
|
formHost
|
||||||
);
|
);
|
||||||
@@ -372,3 +425,4 @@ export function renderImport() {
|
|||||||
refreshJobs(); // resume: show any running/recent conversions immediately
|
refreshJobs(); // resume: show any running/recent conversions immediately
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user