use forge::signal::compactor;
use once_cell::sync::Lazy;
use regex::Regex;
static PULL_PROGRESS_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^pulling [^\n]*\n?").unwrap());
static PROGRESS_BAR_RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?m)^\s*[\[|▕][=>\s]+[\]|▏][^\n]*\n?").unwrap());
static DIGEST_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r"(?m)^(?:verifying sha256 digest|writing manifest|removing any unused layers)[^\n]*\n?",
)
.unwrap()
});
pub fn compress_pull(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let s = PULL_PROGRESS_RE.replace_all(&cleaned, "");
let s = PROGRESS_BAR_RE.replace_all(&s, "");
let s = DIGEST_RE.replace_all(&s, "");
let result = compactor::collapse_blanks(&s);
if result.trim().is_empty() {
return "pulled successfully".to_string();
}
result
}
pub fn compress_list(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let lines: Vec<&str> = cleaned.lines().filter(|l| !l.trim().is_empty()).collect();
if lines.len() > 20 {
return format!(
"{}\n… [{} more models]",
lines[..20].join("\n"),
lines.len() - 20
);
}
lines.join("\n")
}
pub fn compress_show(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let useful: Vec<&str> = cleaned
.lines()
.filter(|l| {
let t = l.trim();
!t.is_empty()
&& !t.starts_with("template")
&& !t.starts_with("system")
&& !t.starts_with("---")
})
.collect();
if useful.len() > 30 {
return format!("{}\n… [truncated]", useful[..30].join("\n"));
}
useful.join("\n")
}
pub fn compress_serve(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let useful: Vec<&str> = cleaned
.lines()
.filter(|l| {
let t = l.trim();
t.contains("Listening on")
|| t.contains("OLLAMA_")
|| t.contains("error")
|| t.contains("Error")
|| t.contains("serving")
})
.collect();
if useful.is_empty() {
return compactor::collapse_blanks(&cleaned);
}
useful.join("\n")
}
pub fn compress_ollama(subcmd: &str, raw: &str) -> String {
let sub = subcmd.trim();
if sub.starts_with("pull") {
return compress_pull(raw);
}
if sub.starts_with("list") || sub == "ls" {
return compress_list(raw);
}
if sub.starts_with("show") {
return compress_show(raw);
}
if sub.starts_with("serve") || sub.starts_with("start") {
return compress_serve(raw);
}
compactor::collapse_blanks(&compactor::normalise(raw))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pull_strips_progress_bars() {
let raw = "pulling manifest\npulling layer sha256:abc123\n[=====> ] 34% ▕ 1.2 GB/3.5 GB\nverifying sha256 digest\nwriting manifest\nremoving any unused layers\nsuccess\n";
let out = compress_pull(raw);
assert!(!out.contains("pulling manifest"), "{out}");
assert!(!out.contains("verifying sha256"), "{out}");
assert!(!out.contains("[=====>"), "{out}");
assert!(
out.contains("success") || out == "pulled successfully",
"{out}"
);
}
#[test]
fn list_truncates_at_20() {
let rows: Vec<String> = (0..25)
.map(|i| format!("model-{i}:latest abc123 4.1 GB 2 hours ago"))
.collect();
let out = compress_list(&rows.join("\n"));
assert!(out.contains("more models"), "{out}");
}
#[test]
fn serve_keeps_listen_line() {
let raw = "time=2024-01-01T00:00:00Z level=INFO source=server.go msg=\"Listening on 127.0.0.1:11434\"\ntime=2024-01-01T00:00:00Z level=INFO source=gpu.go msg=\"no GPU detected\"\n";
let out = compress_serve(raw);
assert!(out.contains("Listening on"), "{out}");
}
#[test]
fn dispatcher_routes_pull() {
let raw = "pulling manifest\nsuccess\n";
let out = compress_ollama("pull llama3", raw);
assert!(!out.contains("pulling manifest"), "{out}");
}
}