Skip to main content

nika_cli/
cache_cmd.rs

1//! `nika cache` subcommand handler.
2
3use clap::Subcommand;
4use colored::Colorize;
5use std::time::Duration;
6
7use nika_daemon::{daemon_socket_path, DaemonClient, DaemonRequest, DaemonResponse};
8use nika_engine::error::NikaError;
9
10#[derive(Subcommand)]
11pub enum CacheAction {
12    /// Show cache statistics
13    Stats,
14    /// Clear all cached responses
15    Clear,
16}
17
18pub async fn handle_cache_command(action: CacheAction) -> Result<(), NikaError> {
19    let client = DaemonClient::new(daemon_socket_path()).with_timeout(Duration::from_secs(5));
20
21    if !client.socket_exists() {
22        eprintln!(
23            "{} daemon not running — start with: nika daemon start",
24            "✗".red().bold()
25        );
26        return Ok(());
27    }
28
29    match action {
30        CacheAction::Stats => {
31            let resp = client
32                .send(DaemonRequest::CacheStats)
33                .await
34                .map_err(cache_err)?;
35
36            match resp {
37                DaemonResponse::CacheStatsResult {
38                    entries,
39                    hits,
40                    misses,
41                    evictions,
42                    total_tokens_saved,
43                    total_cost_saved,
44                } => {
45                    let total = hits + misses;
46                    let hit_rate = if total > 0 {
47                        (hits as f64 / total as f64) * 100.0
48                    } else {
49                        0.0
50                    };
51
52                    println!("{}", "LLM Response Cache".bold());
53                    println!("  entries:      {}", entries);
54                    println!(
55                        "  hit rate:     {:.1}% ({} hits / {} misses)",
56                        hit_rate, hits, misses
57                    );
58                    println!("  evictions:    {}", evictions);
59                    println!("  tokens saved: {}", total_tokens_saved);
60                    println!("  cost saved:   ${:.4}", total_cost_saved);
61                }
62                DaemonResponse::Error { code, message } => {
63                    eprintln!("{} [{code}] {message}", "✗".red().bold());
64                }
65                _ => eprintln!("{} unexpected response", "✗".red().bold()),
66            }
67        }
68
69        CacheAction::Clear => {
70            let resp = client
71                .send(DaemonRequest::CacheClear)
72                .await
73                .map_err(cache_err)?;
74
75            match resp {
76                DaemonResponse::Ok => {
77                    println!("{} cache cleared", "✓".green().bold());
78                }
79                DaemonResponse::Error { code, message } => {
80                    eprintln!("{} [{code}] {message}", "✗".red().bold());
81                }
82                _ => eprintln!("{} unexpected response", "✗".red().bold()),
83            }
84        }
85    }
86
87    Ok(())
88}
89
90fn cache_err(e: nika_daemon::DaemonError) -> NikaError {
91    NikaError::Execution(format!("cache: {e}"))
92}