Mayo commited on
Commit
a423f95
·
unverified ·
1 Parent(s): 8b4809d

fix: shared llama.cpp backend

Browse files
koharu-llm/bin/llm.rs CHANGED
@@ -1,6 +1,9 @@
 
 
1
  use clap::Parser;
2
  use tracing_subscriber::fmt::format::FmtSpan;
3
 
 
4
  use koharu_llm::{GenerateOptions, Language, Llm, ModelId};
5
 
6
  #[derive(Parser, Debug)]
@@ -72,6 +75,7 @@ fn init_tracing() {
72
 
73
  async fn initialize_runtime() -> anyhow::Result<()> {
74
  koharu_runtime::initialize().await?;
 
75
  Ok(())
76
  }
77
 
@@ -81,8 +85,9 @@ async fn main() -> anyhow::Result<()> {
81
 
82
  let args = Args::parse();
83
  initialize_runtime().await?;
 
84
 
85
- let mut llm = Llm::load(args.model, args.cpu).await?;
86
  let target_language = Language::parse(&args.locale).unwrap_or(Language::English);
87
 
88
  let opts = GenerateOptions {
 
1
+ use std::sync::Arc;
2
+
3
  use clap::Parser;
4
  use tracing_subscriber::fmt::format::FmtSpan;
5
 
6
+ use koharu_llm::safe::llama_backend::LlamaBackend;
7
  use koharu_llm::{GenerateOptions, Language, Llm, ModelId};
8
 
9
  #[derive(Parser, Debug)]
 
75
 
76
  async fn initialize_runtime() -> anyhow::Result<()> {
77
  koharu_runtime::initialize().await?;
78
+ koharu_llm::sys::initialize()?;
79
  Ok(())
80
  }
81
 
 
85
 
86
  let args = Args::parse();
87
  initialize_runtime().await?;
88
+ let backend = Arc::new(LlamaBackend::init()?);
89
 
90
+ let mut llm = Llm::load(args.model, args.cpu, backend).await?;
91
  let target_language = Language::parse(&args.locale).unwrap_or(Language::English);
92
 
93
  let opts = GenerateOptions {
koharu-llm/bin/paddleocr-vl.rs CHANGED
@@ -1,9 +1,11 @@
1
  use std::path::{Path, PathBuf};
 
2
  use std::time::Instant;
3
 
4
  use anyhow::{Context, Result, bail};
5
  use clap::{Parser, ValueEnum};
6
  use koharu_llm::paddleocr_vl::{PaddleOcrVl, PaddleOcrVlOutput, PaddleOcrVlTask};
 
7
 
8
  #[derive(Debug, Clone, Copy, ValueEnum)]
9
  enum TaskArg {
@@ -112,18 +114,20 @@ async fn main() -> Result<()> {
112
  koharu_runtime::initialize()
113
  .await
114
  .context("failed to initialize runtime libraries")?;
 
115
 
116
  let cli = Cli::parse();
117
  let task: PaddleOcrVlTask = cli.task.into();
 
118
 
119
  if cli.input.is_empty() && cli.dataset_root.is_none() {
120
  bail!("provide either --input or --dataset-root");
121
  }
122
 
123
  let mut model = if let Some(model_dir) = &cli.model_dir {
124
- PaddleOcrVl::load_from_dir(model_dir, cli.cpu)?
125
  } else {
126
- PaddleOcrVl::load(cli.cpu).await?
127
  };
128
 
129
  if let Some(dataset_root) = &cli.dataset_root {
 
1
  use std::path::{Path, PathBuf};
2
+ use std::sync::Arc;
3
  use std::time::Instant;
4
 
5
  use anyhow::{Context, Result, bail};
6
  use clap::{Parser, ValueEnum};
7
  use koharu_llm::paddleocr_vl::{PaddleOcrVl, PaddleOcrVlOutput, PaddleOcrVlTask};
8
+ use koharu_llm::safe::llama_backend::LlamaBackend;
9
 
10
  #[derive(Debug, Clone, Copy, ValueEnum)]
11
  enum TaskArg {
 
114
  koharu_runtime::initialize()
115
  .await
116
  .context("failed to initialize runtime libraries")?;
117
+ koharu_llm::sys::initialize().context("failed to initialize llama.cpp runtime bindings")?;
118
 
119
  let cli = Cli::parse();
120
  let task: PaddleOcrVlTask = cli.task.into();
121
+ let backend = Arc::new(LlamaBackend::init().context("unable to initialize llama.cpp backend")?);
122
 
123
  if cli.input.is_empty() && cli.dataset_root.is_none() {
124
  bail!("provide either --input or --dataset-root");
125
  }
126
 
127
  let mut model = if let Some(model_dir) = &cli.model_dir {
128
+ PaddleOcrVl::load_from_dir(model_dir, cli.cpu, Arc::clone(&backend))?
129
  } else {
130
+ PaddleOcrVl::load(cli.cpu, backend).await?
131
  };
132
 
133
  if let Some(dataset_root) = &cli.dataset_root {
koharu-llm/src/facade.rs CHANGED
@@ -6,7 +6,8 @@ use tokio::sync::{RwLock, broadcast};
6
  use koharu_types::{Document, LlmState, LlmStateStatus, TextBlock};
7
 
8
  use crate::{
9
- GenerateOptions, Language, Llm, ModelId, language::tags as language_tags, supported_locales,
 
10
  };
11
 
12
  pub use crate::prefetch;
@@ -74,12 +75,7 @@ pub struct Model {
74
  state: Arc<RwLock<State>>,
75
  state_tx: broadcast::Sender<LlmState>,
76
  cpu: bool,
77
- }
78
-
79
- impl Default for Model {
80
- fn default() -> Self {
81
- Self::new(false)
82
- }
83
  }
84
 
85
  pub trait Translatable {
@@ -441,11 +437,12 @@ impl Translatable for TextBlock {
441
  }
442
 
443
  impl Model {
444
- pub fn new(cpu: bool) -> Self {
445
  Self {
446
  state: Arc::new(RwLock::new(State::Empty)),
447
  state_tx: broadcast::channel(64).0,
448
  cpu,
 
449
  }
450
  }
451
 
@@ -482,8 +479,9 @@ impl Model {
482
  let state_cloned = self.state.clone();
483
  let state_tx = self.state_tx.clone();
484
  let cpu = self.cpu;
 
485
  tokio::spawn(async move {
486
- let res = Llm::load(id, cpu).await;
487
  match res {
488
  Ok(llm) => {
489
  let mut guard = state_cloned.write().await;
 
6
  use koharu_types::{Document, LlmState, LlmStateStatus, TextBlock};
7
 
8
  use crate::{
9
+ GenerateOptions, Language, Llm, ModelId, language::tags as language_tags,
10
+ safe::llama_backend::LlamaBackend, supported_locales,
11
  };
12
 
13
  pub use crate::prefetch;
 
75
  state: Arc<RwLock<State>>,
76
  state_tx: broadcast::Sender<LlmState>,
77
  cpu: bool,
78
+ backend: Arc<LlamaBackend>,
 
 
 
 
 
79
  }
80
 
81
  pub trait Translatable {
 
437
  }
438
 
439
  impl Model {
440
+ pub fn new(cpu: bool, backend: Arc<LlamaBackend>) -> Self {
441
  Self {
442
  state: Arc::new(RwLock::new(State::Empty)),
443
  state_tx: broadcast::channel(64).0,
444
  cpu,
445
+ backend,
446
  }
447
  }
448
 
 
479
  let state_cloned = self.state.clone();
480
  let state_tx = self.state_tx.clone();
481
  let cpu = self.cpu;
482
+ let backend = self.backend.clone();
483
  tokio::spawn(async move {
484
+ let res = Llm::load(id, cpu, backend).await;
485
  match res {
486
  Ok(llm) => {
487
  let mut guard = state_cloned.write().await;
koharu-llm/src/model.rs CHANGED
@@ -1,5 +1,6 @@
1
  use std::num::NonZeroU32;
2
  use std::path::PathBuf;
 
3
  use std::sync::Once;
4
  use std::time::Instant;
5
 
@@ -24,7 +25,7 @@ const SAKURA_QWEN_CORRECT_EOS_ID: i32 = 151645;
24
 
25
  pub struct Llm {
26
  model_id: ModelId,
27
- backend: LlamaBackend,
28
  model: LlamaModel,
29
  prompt_renderer: PromptRenderer,
30
  eos_token: LlamaToken,
@@ -58,24 +59,28 @@ impl Default for GenerateOptions {
58
  }
59
 
60
  impl Llm {
61
- pub async fn load(id: ModelId, cpu: bool) -> Result<Self> {
62
  let model_path = id.get().await?;
63
 
64
- tokio::task::spawn_blocking(move || Self::load_from_path(id, cpu, model_path))
65
  .await
66
  .context("failed to join llama.cpp model loading task")?
67
  }
68
 
69
- fn load_from_path(id: ModelId, cpu: bool, model_path: PathBuf) -> Result<Self> {
 
 
 
 
 
70
  crate::sys::initialize().context("failed to initialize llama.cpp runtime bindings")?;
71
 
72
  LOGGING_READY.call_once(|| {
73
  send_logs_to_tracing(LogOptions::default().with_logs_enabled(true));
74
  });
75
 
76
- let backend = LlamaBackend::init().context("unable to initialize llama.cpp backend")?;
77
- let model_params = model_params(cpu, &backend);
78
- let model = LlamaModel::load_from_file(&backend, &model_path, &model_params)
79
  .with_context(|| format!("unable to load model from `{}`", model_path.display()))?;
80
 
81
  let chat_template = model
@@ -126,7 +131,7 @@ impl Llm {
126
  let mut ctx = self
127
  .model
128
  .new_context(
129
- &self.backend,
130
  context_params(prompt_tokens.len(), opts.max_tokens)?,
131
  )
132
  .context("unable to create llama.cpp context")?;
 
1
  use std::num::NonZeroU32;
2
  use std::path::PathBuf;
3
+ use std::sync::Arc;
4
  use std::sync::Once;
5
  use std::time::Instant;
6
 
 
25
 
26
  pub struct Llm {
27
  model_id: ModelId,
28
+ backend: Arc<LlamaBackend>,
29
  model: LlamaModel,
30
  prompt_renderer: PromptRenderer,
31
  eos_token: LlamaToken,
 
59
  }
60
 
61
  impl Llm {
62
+ pub async fn load(id: ModelId, cpu: bool, backend: Arc<LlamaBackend>) -> Result<Self> {
63
  let model_path = id.get().await?;
64
 
65
+ tokio::task::spawn_blocking(move || Self::load_from_path(id, cpu, model_path, backend))
66
  .await
67
  .context("failed to join llama.cpp model loading task")?
68
  }
69
 
70
+ fn load_from_path(
71
+ id: ModelId,
72
+ cpu: bool,
73
+ model_path: PathBuf,
74
+ backend: Arc<LlamaBackend>,
75
+ ) -> Result<Self> {
76
  crate::sys::initialize().context("failed to initialize llama.cpp runtime bindings")?;
77
 
78
  LOGGING_READY.call_once(|| {
79
  send_logs_to_tracing(LogOptions::default().with_logs_enabled(true));
80
  });
81
 
82
+ let model_params = model_params(cpu, backend.as_ref());
83
+ let model = LlamaModel::load_from_file(backend.as_ref(), &model_path, &model_params)
 
84
  .with_context(|| format!("unable to load model from `{}`", model_path.display()))?;
85
 
86
  let chat_template = model
 
131
  let mut ctx = self
132
  .model
133
  .new_context(
134
+ self.backend.as_ref(),
135
  context_params(prompt_tokens.len(), opts.max_tokens)?,
136
  )
137
  .context("unable to create llama.cpp context")?;
koharu-llm/src/paddleocr_vl.rs CHANGED
@@ -1,6 +1,7 @@
1
  use std::ffi::CString;
2
  use std::num::NonZeroU32;
3
  use std::path::{Path, PathBuf};
 
4
  use std::sync::Once;
5
  use std::time::Instant;
6
 
@@ -86,7 +87,7 @@ struct PromptMessage {
86
  }
87
 
88
  pub struct PaddleOcrVl {
89
- backend: LlamaBackend,
90
  model: LlamaModel,
91
  chat_template: String,
92
  bos_token: String,
@@ -96,28 +97,31 @@ pub struct PaddleOcrVl {
96
  }
97
 
98
  impl PaddleOcrVl {
99
- pub async fn load(cpu: bool) -> Result<Self> {
100
  let files = download_model_files().await?;
101
- tokio::task::spawn_blocking(move || Self::load_from_files(files, cpu))
102
  .await
103
  .context("failed to join PaddleOCR-VL loading task")?
104
  }
105
 
106
- pub fn load_from_dir(dir: impl AsRef<Path>, cpu: bool) -> Result<Self> {
 
 
 
 
107
  let files = resolve_local_model_files(dir.as_ref())?;
108
- Self::load_from_files(files, cpu)
109
  }
110
 
111
- fn load_from_files(files: ModelFiles, cpu: bool) -> Result<Self> {
112
  crate::sys::initialize().context("failed to initialize llama.cpp runtime bindings")?;
113
 
114
  LOGGING_READY.call_once(|| {
115
  send_logs_to_tracing(LogOptions::default().with_logs_enabled(true));
116
  });
117
 
118
- let backend = LlamaBackend::init().context("unable to initialize llama.cpp backend")?;
119
- let model_params = model_params(cpu, &backend);
120
- let model = LlamaModel::load_from_file(&backend, &files.model, &model_params)
121
  .with_context(|| format!("unable to load model from `{}`", files.model.display()))?;
122
  let eos_token = model.token_eos();
123
  let chat_template = model
@@ -134,7 +138,7 @@ impl PaddleOcrVl {
134
  mmproj_path,
135
  &model,
136
  &MtmdContextParams {
137
- use_gpu: !cpu && backend.supports_gpu_offload(),
138
  print_timings: true,
139
  n_threads: num_cpus::get().try_into().unwrap_or(i32::MAX),
140
  media_marker: CString::new(DEFAULT_MEDIA_MARKER)
@@ -213,7 +217,7 @@ impl PaddleOcrVl {
213
  )?;
214
  let mut ctx = self
215
  .model
216
- .new_context(&self.backend, ctx_params)
217
  .context("unable to create PaddleOCR-VL llama.cpp context")?;
218
  let n_batch = i32::try_from(batch_tokens).context("batch size exceeds i32")?;
219
 
 
1
  use std::ffi::CString;
2
  use std::num::NonZeroU32;
3
  use std::path::{Path, PathBuf};
4
+ use std::sync::Arc;
5
  use std::sync::Once;
6
  use std::time::Instant;
7
 
 
87
  }
88
 
89
  pub struct PaddleOcrVl {
90
+ backend: Arc<LlamaBackend>,
91
  model: LlamaModel,
92
  chat_template: String,
93
  bos_token: String,
 
97
  }
98
 
99
  impl PaddleOcrVl {
100
+ pub async fn load(cpu: bool, backend: Arc<LlamaBackend>) -> Result<Self> {
101
  let files = download_model_files().await?;
102
+ tokio::task::spawn_blocking(move || Self::load_from_files(files, cpu, backend))
103
  .await
104
  .context("failed to join PaddleOCR-VL loading task")?
105
  }
106
 
107
+ pub fn load_from_dir(
108
+ dir: impl AsRef<Path>,
109
+ cpu: bool,
110
+ backend: Arc<LlamaBackend>,
111
+ ) -> Result<Self> {
112
  let files = resolve_local_model_files(dir.as_ref())?;
113
+ Self::load_from_files(files, cpu, backend)
114
  }
115
 
116
+ fn load_from_files(files: ModelFiles, cpu: bool, backend: Arc<LlamaBackend>) -> Result<Self> {
117
  crate::sys::initialize().context("failed to initialize llama.cpp runtime bindings")?;
118
 
119
  LOGGING_READY.call_once(|| {
120
  send_logs_to_tracing(LogOptions::default().with_logs_enabled(true));
121
  });
122
 
123
+ let model_params = model_params(cpu, backend.as_ref());
124
+ let model = LlamaModel::load_from_file(backend.as_ref(), &files.model, &model_params)
 
125
  .with_context(|| format!("unable to load model from `{}`", files.model.display()))?;
126
  let eos_token = model.token_eos();
127
  let chat_template = model
 
138
  mmproj_path,
139
  &model,
140
  &MtmdContextParams {
141
+ use_gpu: !cpu && backend.as_ref().supports_gpu_offload(),
142
  print_timings: true,
143
  n_threads: num_cpus::get().try_into().unwrap_or(i32::MAX),
144
  media_marker: CString::new(DEFAULT_MEDIA_MARKER)
 
217
  )?;
218
  let mut ctx = self
219
  .model
220
+ .new_context(self.backend.as_ref(), ctx_params)
221
  .context("unable to create PaddleOCR-VL llama.cpp context")?;
222
  let n_batch = i32::try_from(batch_tokens).context("batch size exceeds i32")?;
223
 
koharu-llm/src/sys/mod.rs CHANGED
@@ -62,7 +62,7 @@ const LIB_NAMES: [&str; 4] = [
62
 
63
  static LIBRARIES: OnceLock<LoadedLibraries> = OnceLock::new();
64
 
65
- pub(crate) fn initialize() -> Result<()> {
66
  let runtime_dir = koharu_runtime::llama_runtime_dir().context(
67
  "failed to resolve the llama runtime directory; call `koharu_runtime::initialize()` first",
68
  )?;
 
62
 
63
  static LIBRARIES: OnceLock<LoadedLibraries> = OnceLock::new();
64
 
65
+ pub fn initialize() -> Result<()> {
66
  let runtime_dir = koharu_runtime::llama_runtime_dir().context(
67
  "failed to resolve the llama runtime directory; call `koharu_runtime::initialize()` first",
68
  )?;
koharu-llm/tests/llm.rs CHANGED
@@ -1,7 +1,9 @@
1
  use std::path::PathBuf;
 
2
 
3
  use strum::IntoEnumIterator;
4
 
 
5
  use koharu_llm::{GenerateOptions, Language, Llm, ModelId};
6
 
7
  async fn initialize_runtime() -> anyhow::Result<()> {
@@ -23,9 +25,11 @@ async fn llm_generates_text_for_all_models() -> anyhow::Result<()> {
23
 
24
  koharu_http::hf_hub::set_cache_dir(model_dir)?;
25
  initialize_runtime().await?;
 
 
26
 
27
  for model in ModelId::iter() {
28
- let mut llm = Llm::load(model, false).await?;
29
  let opts = GenerateOptions {
30
  max_tokens: 100,
31
  temperature: 0.3,
 
1
  use std::path::PathBuf;
2
+ use std::sync::Arc;
3
 
4
  use strum::IntoEnumIterator;
5
 
6
+ use koharu_llm::safe::llama_backend::LlamaBackend;
7
  use koharu_llm::{GenerateOptions, Language, Llm, ModelId};
8
 
9
  async fn initialize_runtime() -> anyhow::Result<()> {
 
25
 
26
  koharu_http::hf_hub::set_cache_dir(model_dir)?;
27
  initialize_runtime().await?;
28
+ koharu_llm::sys::initialize()?;
29
+ let backend = Arc::new(LlamaBackend::init()?);
30
 
31
  for model in ModelId::iter() {
32
+ let mut llm = Llm::load(model, false, Arc::clone(&backend)).await?;
33
  let opts = GenerateOptions {
34
  max_tokens: 100,
35
  temperature: 0.3,
koharu-ml/src/facade.rs CHANGED
@@ -1,8 +1,12 @@
1
- use std::{sync::Mutex, time::Instant};
 
 
 
2
 
3
  use anyhow::Result;
4
  use image::DynamicImage;
5
  use koharu_llm::paddleocr_vl::{self as paddleocr_vl_llm, PaddleOcrVl, PaddleOcrVlTask};
 
6
  use koharu_types::{Document, FontPrediction, SerializableDynamicImage, TextBlock, TextDirection};
7
 
8
  use crate::comic_text_detector::{self, ComicTextDetector, crop_text_block_bbox};
@@ -83,11 +87,11 @@ pub struct Model {
83
  }
84
 
85
  impl Model {
86
- pub async fn new(cpu: bool) -> Result<Self> {
87
  Ok(Self {
88
  layout_detector: PPDocLayoutV3::load(cpu).await?,
89
  segmenter: ComicTextDetector::load_segmentation_only(cpu).await?,
90
- ocr: Mutex::new(PaddleOcrVl::load(cpu).await?),
91
  lama: Lama::load(cpu).await?,
92
  font_detector: FontDetector::load(cpu).await?,
93
  })
 
1
+ use std::{
2
+ sync::{Arc, Mutex},
3
+ time::Instant,
4
+ };
5
 
6
  use anyhow::Result;
7
  use image::DynamicImage;
8
  use koharu_llm::paddleocr_vl::{self as paddleocr_vl_llm, PaddleOcrVl, PaddleOcrVlTask};
9
+ use koharu_llm::safe::llama_backend::LlamaBackend;
10
  use koharu_types::{Document, FontPrediction, SerializableDynamicImage, TextBlock, TextDirection};
11
 
12
  use crate::comic_text_detector::{self, ComicTextDetector, crop_text_block_bbox};
 
87
  }
88
 
89
  impl Model {
90
+ pub async fn new(cpu: bool, backend: Arc<LlamaBackend>) -> Result<Self> {
91
  Ok(Self {
92
  layout_detector: PPDocLayoutV3::load(cpu).await?,
93
  segmenter: ComicTextDetector::load_segmentation_only(cpu).await?,
94
+ ocr: Mutex::new(PaddleOcrVl::load(cpu, backend).await?),
95
  lama: Lama::load(cpu).await?,
96
  font_detector: FontDetector::load(cpu).await?,
97
  })
koharu/src/app.rs CHANGED
@@ -2,13 +2,14 @@ use std::{path::PathBuf, sync::Arc};
2
 
3
  use anyhow::{Context, Result};
4
  use clap::Parser;
5
- use once_cell::sync::Lazy;
6
  use rfd::MessageDialog;
7
  use tauri::{Manager, WebviewWindowBuilder};
8
  use tokio::{net::TcpListener, sync::RwLock};
9
  use tracing_subscriber::fmt::format::FmtSpan;
10
 
11
  use koharu_llm::facade;
 
12
  use koharu_ml::{cuda_is_available, device};
13
  use koharu_pipeline::AppResources;
14
  use koharu_renderer::facade::Renderer;
@@ -21,6 +22,7 @@ static APP_ROOT: Lazy<PathBuf> = Lazy::new(|| {
21
  .unwrap_or_default()
22
  });
23
  static MODEL_ROOT: Lazy<PathBuf> = Lazy::new(|| APP_ROOT.join("models"));
 
24
 
25
  #[derive(Parser)]
26
  #[command(version = crate::version::APP_VERSION, about)]
@@ -113,6 +115,15 @@ async fn prefetch() -> Result<()> {
113
  Ok(())
114
  }
115
 
 
 
 
 
 
 
 
 
 
116
  fn warning(headless: bool, title: &str, description: &str) {
117
  tracing::warn!("{description}");
118
 
@@ -173,12 +184,13 @@ async fn build_resources(cpu: bool, headless: bool) -> Result<AppResources> {
173
  tracing::info!("CUDA is available and runtime packages were initialized");
174
  }
175
 
 
176
  let ml = Arc::new(
177
- koharu_ml::facade::Model::new(cpu)
178
  .await
179
  .context("Failed to initialize ML model")?,
180
  );
181
- let llm = Arc::new(facade::Model::new(cpu));
182
  let renderer = Arc::new(Renderer::new().context("Failed to initialize renderer")?);
183
  let state = Arc::new(RwLock::new(State::default()));
184
 
 
2
 
3
  use anyhow::{Context, Result};
4
  use clap::Parser;
5
+ use once_cell::sync::{Lazy, OnceCell};
6
  use rfd::MessageDialog;
7
  use tauri::{Manager, WebviewWindowBuilder};
8
  use tokio::{net::TcpListener, sync::RwLock};
9
  use tracing_subscriber::fmt::format::FmtSpan;
10
 
11
  use koharu_llm::facade;
12
+ use koharu_llm::safe::llama_backend::LlamaBackend;
13
  use koharu_ml::{cuda_is_available, device};
14
  use koharu_pipeline::AppResources;
15
  use koharu_renderer::facade::Renderer;
 
22
  .unwrap_or_default()
23
  });
24
  static MODEL_ROOT: Lazy<PathBuf> = Lazy::new(|| APP_ROOT.join("models"));
25
+ static LLAMA_BACKEND: OnceCell<Arc<LlamaBackend>> = OnceCell::new();
26
 
27
  #[derive(Parser)]
28
  #[command(version = crate::version::APP_VERSION, about)]
 
115
  Ok(())
116
  }
117
 
118
+ fn shared_llama_backend() -> Result<Arc<LlamaBackend>> {
119
+ let backend = LLAMA_BACKEND.get_or_try_init(|| -> Result<Arc<LlamaBackend>> {
120
+ koharu_llm::sys::initialize().context("failed to initialize llama.cpp runtime bindings")?;
121
+ let backend = LlamaBackend::init().context("unable to initialize llama.cpp backend")?;
122
+ Ok(Arc::new(backend))
123
+ })?;
124
+ Ok(Arc::clone(backend))
125
+ }
126
+
127
  fn warning(headless: bool, title: &str, description: &str) {
128
  tracing::warn!("{description}");
129
 
 
184
  tracing::info!("CUDA is available and runtime packages were initialized");
185
  }
186
 
187
+ let llama_backend = shared_llama_backend()?;
188
  let ml = Arc::new(
189
+ koharu_ml::facade::Model::new(cpu, Arc::clone(&llama_backend))
190
  .await
191
  .context("Failed to initialize ML model")?,
192
  );
193
+ let llm = Arc::new(facade::Model::new(cpu, llama_backend));
194
  let renderer = Arc::new(Renderer::new().context("Failed to initialize renderer")?);
195
  let state = Arc::new(RwLock::new(State::default()));
196