use anyhow::Result;
use ollama_rs::generation::completion::request::GenerationRequest;
use ollama_rs::Ollama;
use rand::seq::SliceRandom;
use tokio::sync::mpsc;
use tokio_stream::StreamExt;
pub async fn generate_and_stream_ascii_banner(
ollama_url: &str,
model: &str,
status_tx: mpsc::UnboundedSender<String>,
) -> Result<()> {
if model.is_empty() {
return Ok(());
}
let ollama = Ollama::new(ollama_url.to_string(), 11434);
const NETWORK_CONCEPTS: [&str; 18] = [
"router",
"switch",
"server",
"database",
"firewall",
"load balancer",
"gateway",
"modem",
"bridge",
"proxy",
"DNS",
"VPN",
"tunnel",
"network hub",
"access point",
"packet",
"datagram",
"frame",
];
let selected_concepts: Vec<&str> = {
let mut rng = rand::thread_rng();
NETWORK_CONCEPTS
.choose_multiple(&mut rng, 4)
.copied()
.collect()
};
let prompt = format!(
r#"Generate a small ASCII art illustration depicting network concepts like {}, {}, {}, {}. Output ONLY the ASCII art inside markdown formatting with three backticks, NO explanations, NO blank lines before the art, and NO additional text. Start immediately with the first line of ASCII art. Do not iterate on the art, output the first idea even if not correct."#,
selected_concepts[0], selected_concepts[1], selected_concepts[2], selected_concepts[3]
);
let request = GenerationRequest::new(model.to_string(), prompt);
let stream_result = ollama.generate_stream(request).await;
let mut stream = match stream_result {
Ok(s) => s,
Err(_) => {
return Ok(());
}
};
let mut line_buffer = String::new();
let mut lines_sent = 0;
const MAX_LINES: usize = 50;
let mut content_started = false;
let mut max_line_length = 0;
while let Some(response_result) = stream.next().await {
match response_result {
Ok(responses) => {
for resp in responses {
line_buffer.push_str(&resp.response);
while let Some(newline_pos) = line_buffer.find('\n') {
let line = line_buffer[..newline_pos].to_string();
line_buffer = line_buffer[newline_pos + 1..].to_string();
if line.contains("```") {
continue;
}
if !content_started && line.trim().is_empty() {
continue;
}
if !line.trim().is_empty() {
content_started = true;
}
max_line_length = max_line_length.max(line.trim_end().len());
let _ = status_tx.send(line);
lines_sent += 1;
if lines_sent >= MAX_LINES {
return Ok(());
}
}
}
}
Err(_) => {
return Ok(());
}
}
}
if !line_buffer.is_empty() && lines_sent < MAX_LINES {
if !line_buffer.contains("```") {
max_line_length = max_line_length.max(line_buffer.trim_end().len());
let _ = status_tx.send(line_buffer);
}
}
let spacing = (max_line_length as f32 * 0.7) as usize;
let attribution = format!("{}╰─ artist {}", " ".repeat(spacing), model);
let _ = status_tx.send(attribution);
Ok(())
}