Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c18ddb5475 | ||
|
|
ef9b3738fd |
+9
-1
@@ -3,7 +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.26] - 2026-07-21
|
## [0.2.28] - 2026-07-21
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Quantization Badge & Title for Pre-Converted Models**: Automatically detect quantization (`Q1`, `Q1T`) for ready-to-run `.cmf` models (e.g. `infosave/Bonsai-27Bcmf`) from repo name and downloaded filename, resolving default `Q8_2F` badge fallback. Updated section title to "Загрузки / Конвертации" ("Downloads & Conversions").
|
||||||
|
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Automatic CMF Local Server Lifecycle**: Registering an imported `.cmf` model automatically adds it to `[[cmf.servers]]` and reloads the runtime (`cmf_runtime::manage`), automatically spawning `cortiq serve` on port 8090 so chat completion requests route seamlessly without `upstream unavailable` errors.
|
||||||
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- **Multi-GB Ready CMF Download Stream**: Replaced HTTP stream decoder with a streaming process downloader (`curl`) for pre-converted `.cmf` models to support multi-gigabyte Hugging Face LFS repositories without 30s/60s socket timeouts.
|
- **Multi-GB Ready CMF Download Stream**: Replaced HTTP stream decoder with a streaming process downloader (`curl`) for pre-converted `.cmf` models to support multi-gigabyte Hugging Face LFS repositories without 30s/60s socket timeouts.
|
||||||
|
|||||||
Generated
+1
-1
@@ -285,7 +285,7 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortiq-gateway"
|
name = "cortiq-gateway"
|
||||||
version = "0.2.26"
|
version = "0.2.28"
|
||||||
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.26"
|
version = "0.2.28"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.75"
|
rust-version = "1.75"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|||||||
@@ -1319,6 +1319,18 @@ async fn register_import(State(state): State<SharedState>, Path(job): Path<Strin
|
|||||||
api_key_env: None,
|
api_key_env: None,
|
||||||
caps: Vec::new(),
|
caps: Vec::new(),
|
||||||
});
|
});
|
||||||
|
if cfg.cmf.manage_server {
|
||||||
|
let port = cfg.cmf.local_port;
|
||||||
|
if !cfg.cmf.servers.iter().any(|s| s.model == j.output) {
|
||||||
|
cfg.cmf.servers.push(crate::config::CmfServer {
|
||||||
|
id: id.clone(),
|
||||||
|
model: j.output.clone(),
|
||||||
|
port,
|
||||||
|
threads: cfg.cmf.threads,
|
||||||
|
gpu: cfg.cmf.gpu,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
state.reload(cfg)?;
|
state.reload(cfg)?;
|
||||||
ok(json!({ "ok": true, "model_id": id }))
|
ok(json!({ "ok": true, "model_id": id }))
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-4
@@ -160,6 +160,14 @@ impl JobStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn update_quant(&self, id: &str, quant: String) {
|
||||||
|
if let Some(j) = self.jobs.lock().unwrap().get_mut(id) {
|
||||||
|
j.quant = quant.clone();
|
||||||
|
if !j.log.is_empty() && j.log[0].starts_with("→ downloading ready .cmf model") {
|
||||||
|
j.log[0] = format!("→ downloading ready .cmf model {} ({})", j.repo, quant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fn set_state(&self, id: &str, state: &str) {
|
fn set_state(&self, id: &str, state: &str) {
|
||||||
if let Some(j) = self.jobs.lock().unwrap().get_mut(id) {
|
if let Some(j) = self.jobs.lock().unwrap().get_mut(id) {
|
||||||
j.state = state.into();
|
j.state = state.into();
|
||||||
@@ -321,6 +329,21 @@ async fn download_cmf_repo(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let path_lower = cmf_path.to_ascii_lowercase();
|
||||||
|
let detected_quant =
|
||||||
|
if path_lower.contains("-q1t.") || repo.to_ascii_lowercase().contains("2bit") {
|
||||||
|
"Q1T".to_string()
|
||||||
|
} else if path_lower.contains("-q1.") || repo.to_ascii_lowercase().contains("cmf") {
|
||||||
|
"Q1".to_string()
|
||||||
|
} else if path_lower.contains("-q4.") {
|
||||||
|
"Q4".to_string()
|
||||||
|
} else if path_lower.contains("-q8.") {
|
||||||
|
"Q8".to_string()
|
||||||
|
} else {
|
||||||
|
"READY_CMF".to_string()
|
||||||
|
};
|
||||||
|
store.update_quant(&id, detected_quant);
|
||||||
|
|
||||||
store.push_line(&id, format!("→ downloading {cmf_path} from HuggingFace..."));
|
store.push_line(&id, format!("→ downloading {cmf_path} from HuggingFace..."));
|
||||||
store.set_progress(&id, 0.05, "downloading".into());
|
store.set_progress(&id, 0.05, "downloading".into());
|
||||||
|
|
||||||
@@ -464,12 +487,19 @@ pub fn start_import(store: Arc<JobStore>, cfg: &CmfCfg, p: ImportParams) -> Resu
|
|||||||
|
|
||||||
let id = gen_id(&p.repo);
|
let id = gen_id(&p.repo);
|
||||||
let is_cmf = is_cmf_repo(&p.repo);
|
let is_cmf = is_cmf_repo(&p.repo);
|
||||||
let quant_display = if p.quant.is_empty() || p.quant == "auto" {
|
let quant_display = if is_cmf {
|
||||||
if is_cmf {
|
let r = p.repo.to_lowercase();
|
||||||
"READY_CMF".to_string()
|
if r.contains("2bit") || r.contains("q1t") {
|
||||||
|
"Q1T".to_string()
|
||||||
|
} else if r.contains("1.7bcmf") || r.contains("27bcmf") || r.contains("cmf") {
|
||||||
|
"Q1".to_string()
|
||||||
|
} else if p.quant.is_empty() || p.quant == "auto" || p.quant == "Q8_2F" {
|
||||||
|
"Q1".to_string()
|
||||||
} else {
|
} else {
|
||||||
"q8".to_string()
|
p.quant.clone()
|
||||||
}
|
}
|
||||||
|
} else if p.quant.is_empty() || p.quant == "auto" {
|
||||||
|
"q8".to_string()
|
||||||
} else {
|
} else {
|
||||||
p.quant.clone()
|
p.quant.clone()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -145,6 +145,14 @@ impl AppState {
|
|||||||
let live = Live::build(new_cfg.clone(), &self.secrets, self.router_status.clone())?;
|
let live = Live::build(new_cfg.clone(), &self.secrets, self.router_status.clone())?;
|
||||||
new_cfg.save(&self.config_path)?;
|
new_cfg.save(&self.config_path)?;
|
||||||
self.live.store(Arc::new(live));
|
self.live.store(Arc::new(live));
|
||||||
|
|
||||||
|
let cmf_rt = self.cmf.clone();
|
||||||
|
let cmf_cfg = new_cfg.cmf.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
cmf_rt.stop().await;
|
||||||
|
crate::cmf_runtime::manage(cmf_rt, cmf_cfg).await;
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -374,7 +374,7 @@ export default {
|
|||||||
"import.failed": "Conversion failed — see the log.",
|
"import.failed": "Conversion failed — see the log.",
|
||||||
"import.register": "Register as local model",
|
"import.register": "Register as local model",
|
||||||
"import.registered": "Registered as '{id}'. Run cortiq-server to serve it.",
|
"import.registered": "Registered as '{id}'. Run cortiq-server to serve it.",
|
||||||
"import.jobs": "Conversions",
|
"import.jobs": "Downloads & Conversions",
|
||||||
"import.cancel": "Cancel",
|
"import.cancel": "Cancel",
|
||||||
"import.cancelling": "Cancelling — cleaning up partial output…",
|
"import.cancelling": "Cancelling — cleaning up partial output…",
|
||||||
"import.delete": "Delete",
|
"import.delete": "Delete",
|
||||||
|
|||||||
Vendored
+1
-1
@@ -373,7 +373,7 @@ export default {
|
|||||||
"import.failed": "Конвертация не удалась — смотрите лог.",
|
"import.failed": "Конвертация не удалась — смотрите лог.",
|
||||||
"import.register": "Подключить как локальную модель",
|
"import.register": "Подключить как локальную модель",
|
||||||
"import.registered": "Подключено как '{id}'. Запустите cortiq-server для обслуживания.",
|
"import.registered": "Подключено как '{id}'. Запустите cortiq-server для обслуживания.",
|
||||||
"import.jobs": "Конвертации",
|
"import.jobs": "Загрузки / Конвертации",
|
||||||
"import.cancel": "Отменить",
|
"import.cancel": "Отменить",
|
||||||
"import.cancelling": "Отмена — удаляю недописанный файл…",
|
"import.cancelling": "Отмена — удаляю недописанный файл…",
|
||||||
"import.delete": "Удалить",
|
"import.delete": "Удалить",
|
||||||
|
|||||||
Reference in New Issue
Block a user