Mayo commited on
fix: shared llama.cpp backend
Browse files- koharu-llm/bin/llm.rs +6 -1
- koharu-llm/bin/paddleocr-vl.rs +6 -2
- koharu-llm/src/facade.rs +7 -9
- koharu-llm/src/model.rs +13 -8
- koharu-llm/src/paddleocr_vl.rs +15 -11
- koharu-llm/src/sys/mod.rs +1 -1
- koharu-llm/tests/llm.rs +5 -1
- koharu-ml/src/facade.rs +7 -3
- koharu/src/app.rs +15 -3
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,
|
|
|
|
| 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(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 77 |
-
let
|
| 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 |
-
|
| 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(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 119 |
-
let
|
| 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(
|
| 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
|
| 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::{
|
|
|
|
|
|
|
|
|
|
| 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 |
|