@@ -330,14 +330,25 @@ fn compute_fingerprint(features: &[String]) -> String {
330330
331331fn resolve_shared_model_cache_dir_from_env (
332332 rpg_model_cache_dir : Option < & std:: ffi:: OsStr > ,
333+ hf_hub_cache : Option < & std:: ffi:: OsStr > ,
334+ hf_home : Option < & std:: ffi:: OsStr > ,
333335 xdg_cache_home : Option < & std:: ffi:: OsStr > ,
334336 home : Option < & std:: ffi:: OsStr > ,
335337 userprofile : Option < & std:: ffi:: OsStr > ,
336338) -> Result < PathBuf > {
339+ // Priority 1: explicit RPG override
337340 if let Some ( path) = rpg_model_cache_dir {
338341 return Ok ( PathBuf :: from ( path) ) ;
339342 }
340-
343+ // Priority 2: HuggingFace Hub cache (points directly at hub cache root)
344+ if let Some ( path) = hf_hub_cache {
345+ return Ok ( PathBuf :: from ( path) ) ;
346+ }
347+ // Priority 3: HF_HOME (model repos live directly under this directory)
348+ if let Some ( path) = hf_home {
349+ return Ok ( PathBuf :: from ( path) ) ;
350+ }
351+ // Priority 4: default ~/.cache/rpg-encoder/models/fastembed
341352 let home_dir = home
342353 . map ( PathBuf :: from)
343354 . or_else ( || userprofile. map ( PathBuf :: from) )
@@ -357,6 +368,8 @@ fn resolve_shared_model_cache_dir_from_env(
357368fn shared_model_cache_dir ( ) -> Result < PathBuf > {
358369 let cache_dir = resolve_shared_model_cache_dir_from_env (
359370 std:: env:: var_os ( "RPG_MODEL_CACHE_DIR" ) . as_deref ( ) ,
371+ std:: env:: var_os ( "HUGGINGFACE_HUB_CACHE" ) . as_deref ( ) ,
372+ std:: env:: var_os ( "HF_HOME" ) . as_deref ( ) ,
360373 std:: env:: var_os ( "XDG_CACHE_HOME" ) . as_deref ( ) ,
361374 std:: env:: var_os ( "HOME" ) . as_deref ( ) ,
362375 std:: env:: var_os ( "USERPROFILE" ) . as_deref ( ) ,
@@ -366,11 +379,15 @@ fn shared_model_cache_dir() -> Result<PathBuf> {
366379}
367380
368381/// Initialize the fastembed model with a shared machine-level cache.
382+ ///
383+ /// IMPORTANT: Do NOT add `.with_show_download_progress(true)` — the MCP server
384+ /// uses stdio for JSON-RPC transport and any stdout/stderr noise from progress
385+ /// bars can corrupt the protocol stream, causing deadlocks and zombie processes.
369386fn init_model ( ) -> Result < TextEmbedding > {
370387 let cache_dir = shared_model_cache_dir ( ) ?;
371- let options = fastembed :: TextInitOptions :: new ( EmbeddingModel :: BGESmallENV15 )
372- . with_show_download_progress ( true )
373- . with_cache_dir ( cache_dir) ;
388+
389+ let options =
390+ fastembed :: TextInitOptions :: new ( EmbeddingModel :: BGESmallENV15 ) . with_cache_dir ( cache_dir) ;
374391
375392 let model = TextEmbedding :: try_new ( options)
376393 . context ( "failed to initialize embedding model (BGE-small-en-v1.5)" ) ?;
@@ -569,6 +586,8 @@ mod tests {
569586 fn test_resolve_shared_model_cache_dir_prefers_explicit_override ( ) {
570587 let path = resolve_shared_model_cache_dir_from_env (
571588 Some ( std:: ffi:: OsStr :: new ( "D:/cache/rpg-models" ) ) ,
589+ Some ( std:: ffi:: OsStr :: new ( "C:/hf-hub-cache" ) ) ,
590+ Some ( std:: ffi:: OsStr :: new ( "C:/hf-home" ) ) ,
572591 Some ( std:: ffi:: OsStr :: new ( "/tmp/xdg-cache" ) ) ,
573592 Some ( std:: ffi:: OsStr :: new ( "/home/tester" ) ) ,
574593 Some ( std:: ffi:: OsStr :: new ( "C:/Users/tester" ) ) ,
@@ -580,6 +599,8 @@ mod tests {
580599 #[ test]
581600 fn test_resolve_shared_model_cache_dir_uses_xdg_on_linux ( ) {
582601 let path = resolve_shared_model_cache_dir_from_env (
602+ None ,
603+ None ,
583604 None ,
584605 Some ( std:: ffi:: OsStr :: new ( "/tmp/xdg-cache" ) ) ,
585606 Some ( std:: ffi:: OsStr :: new ( "/home/tester" ) ) ,
@@ -603,6 +624,8 @@ mod tests {
603624 #[ test]
604625 fn test_resolve_shared_model_cache_dir_falls_back_to_home_cache ( ) {
605626 let path = resolve_shared_model_cache_dir_from_env (
627+ None ,
628+ None ,
606629 None ,
607630 None ,
608631 Some ( std:: ffi:: OsStr :: new ( "/home/tester" ) ) ,
@@ -618,6 +641,8 @@ mod tests {
618641 #[ test]
619642 fn test_resolve_shared_model_cache_dir_uses_userprofile_when_home_missing ( ) {
620643 let path = resolve_shared_model_cache_dir_from_env (
644+ None ,
645+ None ,
621646 None ,
622647 None ,
623648 None ,
@@ -632,13 +657,56 @@ mod tests {
632657
633658 #[ test]
634659 fn test_resolve_shared_model_cache_dir_errors_without_home_or_override ( ) {
635- let err = resolve_shared_model_cache_dir_from_env ( None , None , None , None ) . unwrap_err ( ) ;
660+ let err = resolve_shared_model_cache_dir_from_env ( None , None , None , None , None , None )
661+ . unwrap_err ( ) ;
636662 assert ! (
637663 err. to_string( )
638664 . contains( "could not determine home directory" )
639665 ) ;
640666 }
641667
668+ #[ test]
669+ fn test_resolve_prefers_hf_hub_cache_over_hf_home ( ) {
670+ let path = resolve_shared_model_cache_dir_from_env (
671+ None ,
672+ Some ( std:: ffi:: OsStr :: new ( "C:/hf-hub-cache" ) ) ,
673+ Some ( std:: ffi:: OsStr :: new ( "C:/hf-home" ) ) ,
674+ None ,
675+ Some ( std:: ffi:: OsStr :: new ( "/home/tester" ) ) ,
676+ None ,
677+ )
678+ . unwrap ( ) ;
679+ assert_eq ! ( path, PathBuf :: from( "C:/hf-hub-cache" ) ) ;
680+ }
681+
682+ #[ test]
683+ fn test_resolve_uses_hf_home_when_no_explicit_hub_cache ( ) {
684+ let path = resolve_shared_model_cache_dir_from_env (
685+ None ,
686+ None ,
687+ Some ( std:: ffi:: OsStr :: new ( "C:/Models_LLM" ) ) ,
688+ None ,
689+ Some ( std:: ffi:: OsStr :: new ( "/home/tester" ) ) ,
690+ None ,
691+ )
692+ . unwrap ( ) ;
693+ assert_eq ! ( path, PathBuf :: from( "C:/Models_LLM" ) ) ;
694+ }
695+
696+ #[ test]
697+ fn test_resolve_rpg_override_beats_hf_vars ( ) {
698+ let path = resolve_shared_model_cache_dir_from_env (
699+ Some ( std:: ffi:: OsStr :: new ( "D:/rpg-models" ) ) ,
700+ Some ( std:: ffi:: OsStr :: new ( "C:/hf-hub-cache" ) ) ,
701+ Some ( std:: ffi:: OsStr :: new ( "C:/hf-home" ) ) ,
702+ None ,
703+ Some ( std:: ffi:: OsStr :: new ( "/home/tester" ) ) ,
704+ None ,
705+ )
706+ . unwrap ( ) ;
707+ assert_eq ! ( path, PathBuf :: from( "D:/rpg-models" ) ) ;
708+ }
709+
642710 #[ test]
643711 fn test_cosine_similarity_identical ( ) {
644712 let a = vec ! [ 1.0 , 0.0 , 0.0 ] ;
0 commit comments