sqlite_graphrag/commands/
daemon.rs1use crate::constants::DAEMON_IDLE_SHUTDOWN_SECS;
2use crate::errors::AppError;
3use crate::output;
4use crate::paths::AppPaths;
5
6#[derive(clap::Args)]
7#[command(after_long_help = "EXAMPLES:\n \
8 # Start the embedding daemon in the foreground (default 600s idle timeout)\n \
9 sqlite-graphrag daemon\n\n \
10 # Start with a longer idle timeout for batch ingestion\n \
11 sqlite-graphrag daemon --idle-shutdown-secs 3600\n\n \
12 # Health-check a running daemon (exit 4 if not running)\n \
13 sqlite-graphrag daemon --ping\n\n \
14 # Request graceful shutdown of a running daemon\n \
15 sqlite-graphrag daemon --stop\n\n\
16AUTO-SPAWN BEHAVIOR:\n \
17 recall and hybrid-search spawn a daemon automatically when none is running,\n \
18 amortising model warm-up across multiple invocations (idle timeout 600s).\n\n \
19 Disable per-invocation: sqlite-graphrag recall \"query\" --autostart-daemon=false\n \
20 Disable globally: export SQLITE_GRAPHRAG_DAEMON_DISABLE_AUTOSTART=1\n\n \
21 The --autostart-daemon flag takes precedence over the env var.")]
22pub struct DaemonArgs {
23 #[arg(long, default_value_t = DAEMON_IDLE_SHUTDOWN_SECS)]
26 pub idle_shutdown_secs: u64,
27 #[arg(long)]
29 pub ping: bool,
30 #[arg(long)]
32 pub stop: bool,
33 #[arg(
35 long,
36 env = "SQLITE_GRAPHRAG_SHUTDOWN_TIMEOUT_SECS",
37 default_value_t = 10
38 )]
39 pub shutdown_timeout_secs: u64,
40 #[arg(long, hide = true, help = "No-op; JSON is always emitted on stdout")]
41 pub json: bool,
42 #[arg(long, env = "SQLITE_GRAPHRAG_DB_PATH")]
43 pub db: Option<String>,
44}
45
46pub fn run(args: DaemonArgs) -> Result<(), AppError> {
47 let _ = args.json;
48 let paths = AppPaths::resolve(args.db.as_deref())?;
49 paths.ensure_dirs()?;
50
51 if args.ping {
52 let response = crate::daemon::try_ping(&paths.models)?
53 .ok_or_else(|| AppError::NotFound("daemon not running".to_string()))?;
54 if let crate::daemon::DaemonResponse::Ok { ref version, .. } = response {
55 if version != crate::constants::SQLITE_GRAPHRAG_VERSION {
56 tracing::warn!(target: "daemon_cmd",
57 daemon_version = %version,
58 cli_version = crate::constants::SQLITE_GRAPHRAG_VERSION,
59 "daemon version mismatch; auto-restart will occur on the next embedding request"
60 );
61 }
62 }
63 output::emit_json(&response)?;
64 return Ok(());
65 }
66
67 if args.stop {
68 let response = crate::daemon::try_shutdown(&paths.models)?
69 .ok_or_else(|| AppError::NotFound("daemon not running".to_string()))?;
70 output::emit_json(&response)?;
71 return Ok(());
72 }
73
74 crate::daemon::run(
75 &paths.models,
76 args.idle_shutdown_secs,
77 args.shutdown_timeout_secs,
78 )
79}