systemprompt_logging/services/cli/
banners.rs1use std::io::Write;
7use std::time::Duration;
8
9use indicatif::{ProgressBar, ProgressStyle};
10use systemprompt_traits::LogEventLevel;
11
12use super::output::publish_log;
13use super::service::CliService;
14use super::startup::{
15 render_phase_header, render_phase_info, render_phase_success, render_phase_warning,
16 render_startup_banner,
17};
18use super::table::render_startup_complete;
19use super::theme::{EmphasisType, Theme};
20
21impl CliService {
22 pub fn startup_banner(subtitle: Option<&str>) {
23 render_startup_banner(subtitle);
24 }
25
26 pub fn phase(name: &str) {
27 publish_log(LogEventLevel::Info, "cli", &format!("Phase: {}", name));
28 render_phase_header(name);
29 }
30
31 pub fn phase_success(message: &str, detail: Option<&str>) {
32 publish_log(LogEventLevel::Info, "cli", message);
33 render_phase_success(message, detail);
34 }
35
36 pub fn phase_info(message: &str, detail: Option<&str>) {
37 publish_log(LogEventLevel::Info, "cli", message);
38 render_phase_info(message, detail);
39 }
40
41 pub fn phase_warning(message: &str, detail: Option<&str>) {
42 publish_log(LogEventLevel::Warn, "cli", message);
43 render_phase_warning(message, detail);
44 }
45
46 pub fn service_spinner(service_name: &str, port: Option<u16>) -> ProgressBar {
47 let msg = port.map_or_else(
48 || format!("Starting {}", service_name),
49 |p| format!("Starting {} on :{}", service_name, p),
50 );
51 let pb = ProgressBar::new_spinner();
52 let spinner_template = concat!("{spinner:.208}", " {msg}");
53 pb.set_style(
54 ProgressStyle::default_spinner()
55 .template(spinner_template)
56 .unwrap_or_else(|_| ProgressStyle::default_spinner()),
57 );
58 pb.set_message(msg);
59 pb.enable_steady_tick(Duration::from_millis(80));
60 pb
61 }
62
63 pub fn startup_complete(duration: Duration, api_url: &str) {
64 publish_log(
65 LogEventLevel::Info,
66 "cli",
67 &format!("Startup complete in {:.1}s", duration.as_secs_f64()),
68 );
69 render_startup_complete(duration, api_url);
70 }
71
72 pub fn session_context(
73 profile: &str,
74 session_id: &systemprompt_identifiers::SessionId,
75 tenant: Option<&str>,
76 ) {
77 Self::session_context_with_url(profile, session_id, tenant, None);
78 }
79
80 pub fn session_context_with_url(
81 profile: &str,
82 session_id: &systemprompt_identifiers::SessionId,
83 tenant: Option<&str>,
84 api_url: Option<&str>,
85 ) {
86 let session_str = session_id.as_str();
87 let truncated_session = session_str
88 .get(..12)
89 .map_or_else(|| session_str.to_owned(), |s| format!("{}...", s));
90
91 let tenant_info = tenant.map_or_else(String::new, |t| format!(" | tenant: {}", t));
92
93 let url_info = api_url.map_or_else(String::new, |u| format!(" | {}", u));
94
95 let banner = format!(
96 "[profile: {} | session: {}{}{}]",
97 profile, truncated_session, tenant_info, url_info
98 );
99
100 let mut stderr = std::io::stderr();
101 writeln!(stderr, "{}", Theme::color(&banner, EmphasisType::Dim)).ok();
102 }
103
104 pub fn profile_banner(profile_name: &str, is_cloud: bool, tenant: Option<&str>) {
105 let target_label = if is_cloud { "cloud" } else { "local" };
106 let tenant_info = tenant.map_or_else(String::new, |t| format!(" | tenant: {}", t));
107 let banner = format!(
108 "[profile: {} ({}){}]",
109 profile_name, target_label, tenant_info
110 );
111 let mut stderr = std::io::stderr();
112 writeln!(stderr, "{}", Theme::color(&banner, EmphasisType::Dim)).ok();
113 }
114}