Mayo commited on
Commit
8f6c8ee
·
unverified ·
1 Parent(s): 7628596

chore: add more context info

Browse files
Cargo.lock CHANGED
@@ -4131,8 +4131,6 @@ dependencies = [
4131
  "objc2-metal-performance-shaders",
4132
  "objc2-metal-performance-shaders-graph",
4133
  "once_cell",
4134
- "rayon",
4135
- "reqwest",
4136
  "rustfft",
4137
  "serde",
4138
  "serde_json",
@@ -4181,7 +4179,6 @@ dependencies = [
4181
  "rayon",
4182
  "reqwest",
4183
  "serde_json",
4184
- "strum",
4185
  "tempfile",
4186
  "tokio",
4187
  "tracing",
 
4131
  "objc2-metal-performance-shaders",
4132
  "objc2-metal-performance-shaders-graph",
4133
  "once_cell",
 
 
4134
  "rustfft",
4135
  "serde",
4136
  "serde_json",
 
4179
  "rayon",
4180
  "reqwest",
4181
  "serde_json",
 
4182
  "tempfile",
4183
  "tokio",
4184
  "tracing",
README.md CHANGED
@@ -39,12 +39,12 @@ Koharu can be run in headless mode via command line.
39
 
40
  ```bash
41
  # macOS / Linux
42
- koharu -b 0.0.0.0:5003
43
  # Windows
44
- koharu.exe -b 0.0.0.0:5003
45
  ```
46
 
47
- You can now access Koharu Web UI at `http://<your-server-ip>:5003`.
48
 
49
  ### File association
50
 
 
39
 
40
  ```bash
41
  # macOS / Linux
42
+ koharu --port 4000 --headless
43
  # Windows
44
+ koharu.exe --port 4000 --headless
45
  ```
46
 
47
+ You can now access Koharu Web UI at `http://<your-server-ip>:4000`.
48
 
49
  ### File association
50
 
koharu-ml/Cargo.toml CHANGED
@@ -29,9 +29,7 @@ tracing = { workspace = true }
29
  tracing-subscriber = { workspace = true }
30
  tokio = { workspace = true }
31
  rustfft = { workspace = true }
32
- reqwest = { workspace = true }
33
  once_cell = { workspace = true }
34
- rayon = { workspace = true }
35
  dirs = { workspace = true }
36
  futures = { workspace = true }
37
  num_cpus = { workspace = true }
 
29
  tracing-subscriber = { workspace = true }
30
  tokio = { workspace = true }
31
  rustfft = { workspace = true }
 
32
  once_cell = { workspace = true }
 
33
  dirs = { workspace = true }
34
  futures = { workspace = true }
35
  num_cpus = { workspace = true }
koharu-runtime/Cargo.toml CHANGED
@@ -24,7 +24,6 @@ futures = { workspace = true }
24
  once_cell = { workspace = true }
25
  libloading = { workspace = true }
26
  num_cpus = { workspace = true }
27
- strum = { workspace = true }
28
  csv = { workspace = true }
29
  flate2 = { workspace = true }
30
 
 
24
  once_cell = { workspace = true }
25
  libloading = { workspace = true }
26
  num_cpus = { workspace = true }
 
27
  csv = { workspace = true }
28
  flate2 = { workspace = true }
29
 
koharu-runtime/src/dylib.rs CHANGED
@@ -5,7 +5,7 @@ use std::{
5
  sync::Arc,
6
  };
7
 
8
- use anyhow::Result;
9
  use futures::stream::{self, StreamExt, TryStreamExt};
10
  use koharu_core::http::{http_client, http_download};
11
  use once_cell::sync::OnceCell;
@@ -13,7 +13,7 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
13
  use tokio::task;
14
  use tracing::debug;
15
 
16
- use crate::{pypi::PYPI_ENDPOINT, zip::fetch_record};
17
 
18
  /// Keep handles to loaded dynamic libraries alive for process lifetime
19
  static DYLIB_HANDLES: OnceCell<Vec<libloading::Library>> = OnceCell::new();
@@ -226,9 +226,16 @@ fn current_platform_tags() -> Result<&'static [&'static str]> {
226
 
227
  async fn fetch_and_extract(pkg: &str, platform_tags: &[&str], out_dir: Arc<PathBuf>) -> Result<()> {
228
  // 1) Query PyPI JSON
229
- let meta_url = format!("{}/pypi/{pkg}/json", *PYPI_ENDPOINT);
230
- let resp = http_client().get(&meta_url).send().await?;
231
- let json: serde_json::Value = resp.json().await?;
 
 
 
 
 
 
 
232
 
233
  // 2) Choose a wheel
234
  let files = json
@@ -239,7 +246,6 @@ async fn fetch_and_extract(pkg: &str, platform_tags: &[&str], out_dir: Arc<PathB
239
  for f in files {
240
  let filename = f.get("filename").and_then(|v| v.as_str()).unwrap_or("");
241
  let file_url = f.get("url").and_then(|v| v.as_str()).unwrap_or("");
242
- let file_url = PYPI_ENDPOINT.refine_url(file_url);
243
 
244
  if !filename.ends_with(".whl") {
245
  continue;
@@ -253,7 +259,9 @@ async fn fetch_and_extract(pkg: &str, platform_tags: &[&str], out_dir: Arc<PathB
253
  debug!("{pkg}: selected wheel {wheel_name}");
254
 
255
  // 3) Use RECORD to check local dylibs; download only if needed
256
- let entries = fetch_record(&wheel_url).await?;
 
 
257
 
258
  // Fast path: existence + size-only check; no hashing.
259
  // If size is None and file exists, treat as OK (no further verification).
@@ -273,10 +281,14 @@ async fn fetch_and_extract(pkg: &str, platform_tags: &[&str], out_dir: Arc<PathB
273
 
274
  if needs_download {
275
  debug!("{pkg}: downloading {wheel_name}...");
276
- let bytes = http_download(&wheel_url).await?;
 
 
277
  let out = Arc::clone(&out_dir);
278
 
279
- task::spawn_blocking(move || extract_from_wheel(&bytes, out.as_ref())).await??;
 
 
280
  debug!("{pkg}: download and extract complete");
281
  Ok(())
282
  } else {
 
5
  sync::Arc,
6
  };
7
 
8
+ use anyhow::{Context, Result};
9
  use futures::stream::{self, StreamExt, TryStreamExt};
10
  use koharu_core::http::{http_client, http_download};
11
  use once_cell::sync::OnceCell;
 
13
  use tokio::task;
14
  use tracing::debug;
15
 
16
+ use crate::zip::fetch_record;
17
 
18
  /// Keep handles to loaded dynamic libraries alive for process lifetime
19
  static DYLIB_HANDLES: OnceCell<Vec<libloading::Library>> = OnceCell::new();
 
226
 
227
  async fn fetch_and_extract(pkg: &str, platform_tags: &[&str], out_dir: Arc<PathBuf>) -> Result<()> {
228
  // 1) Query PyPI JSON
229
+ let meta_url = format!("https://pypi.org/pypi/{pkg}/json");
230
+ let resp = http_client()
231
+ .get(&meta_url)
232
+ .send()
233
+ .await
234
+ .context("failed to fetch package metadata")?;
235
+ let json: serde_json::Value = resp
236
+ .json()
237
+ .await
238
+ .context("failed to parse package metadata")?;
239
 
240
  // 2) Choose a wheel
241
  let files = json
 
246
  for f in files {
247
  let filename = f.get("filename").and_then(|v| v.as_str()).unwrap_or("");
248
  let file_url = f.get("url").and_then(|v| v.as_str()).unwrap_or("");
 
249
 
250
  if !filename.ends_with(".whl") {
251
  continue;
 
259
  debug!("{pkg}: selected wheel {wheel_name}");
260
 
261
  // 3) Use RECORD to check local dylibs; download only if needed
262
+ let entries = fetch_record(&wheel_url)
263
+ .await
264
+ .context("failed to fetch RECORD")?;
265
 
266
  // Fast path: existence + size-only check; no hashing.
267
  // If size is None and file exists, treat as OK (no further verification).
 
281
 
282
  if needs_download {
283
  debug!("{pkg}: downloading {wheel_name}...");
284
+ let bytes = http_download(&wheel_url)
285
+ .await
286
+ .context("failed to download wheel")?;
287
  let out = Arc::clone(&out_dir);
288
 
289
+ task::spawn_blocking(move || extract_from_wheel(&bytes, out.as_ref()))
290
+ .await?
291
+ .context("failed to extract dylibs")?;
292
  debug!("{pkg}: download and extract complete");
293
  Ok(())
294
  } else {
koharu-runtime/src/lib.rs CHANGED
@@ -1,6 +1,5 @@
1
  mod dylib;
2
  mod fonts;
3
- mod pypi;
4
  mod zip;
5
 
6
  pub use dylib::{ensure_dylibs, preload_dylibs};
 
1
  mod dylib;
2
  mod fonts;
 
3
  mod zip;
4
 
5
  pub use dylib::{ensure_dylibs, preload_dylibs};
koharu-runtime/src/pypi.rs DELETED
@@ -1,44 +0,0 @@
1
- use std::time::{Duration, Instant};
2
-
3
- use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
4
- use strum::Display;
5
-
6
- pub static PYPI_ENDPOINT: once_cell::sync::Lazy<&PypiEndpoint> = once_cell::sync::Lazy::new(|| {
7
- [PypiEndpoint::Official, PypiEndpoint::Tsinghua]
8
- .par_iter()
9
- .map(|endpoint| {
10
- let start = Instant::now();
11
- let url = format!("{endpoint}/pypi/sampleproject/json");
12
- let resp = reqwest::blocking::get(&url);
13
- match resp {
14
- Ok(resp) if resp.status().is_success() => {
15
- let duration = start.elapsed();
16
- (duration, endpoint)
17
- }
18
- _ => (Duration::MAX, endpoint),
19
- }
20
- })
21
- .min_by_key(|(duration, _)| *duration)
22
- .map(|(_, endpoint)| endpoint)
23
- .unwrap_or_else(|| &PypiEndpoint::Official)
24
- });
25
-
26
- #[derive(Debug, Clone, Display)]
27
- pub enum PypiEndpoint {
28
- #[strum(serialize = "https://pypi.org")]
29
- Official,
30
- #[strum(serialize = "https://mirrors.tuna.tsinghua.edu.cn")]
31
- Tsinghua,
32
- }
33
-
34
- impl PypiEndpoint {
35
- pub fn refine_url(&self, path: &str) -> String {
36
- match self {
37
- PypiEndpoint::Tsinghua => path.replace(
38
- "https://files.pythonhosted.org",
39
- format!("{self}/pypi/web").as_str(),
40
- ),
41
- _ => path.to_string(),
42
- }
43
- }
44
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
koharu/src/app.rs CHANGED
@@ -6,7 +6,7 @@ use std::{
6
  },
7
  };
8
 
9
- use anyhow::Result;
10
  use clap::Parser;
11
  use koharu_ml::{DeviceName, cuda_is_available, device_name};
12
  use koharu_runtime::{ensure_dylibs, preload_dylibs};
@@ -170,10 +170,12 @@ async fn prefetch() -> Result<()> {
170
  Ok(())
171
  }
172
 
173
- async fn build_resources(use_cpu: bool, _register_file_assoc: bool) -> Result<AppResources> {
174
  if cuda_is_available() {
175
- ensure_dylibs(LIB_ROOT.to_path_buf()).await?;
176
- preload_dylibs(LIB_ROOT.to_path_buf())?;
 
 
177
 
178
  #[cfg(target_os = "windows")]
179
  {
@@ -181,7 +183,7 @@ async fn build_resources(use_cpu: bool, _register_file_assoc: bool) -> Result<Ap
181
  tracing::warn!(?err, "Failed to register .khr file association");
182
  }
183
 
184
- crate::windows::add_dll_directory(&LIB_ROOT)?;
185
  }
186
 
187
  tracing::info!(
@@ -190,10 +192,14 @@ async fn build_resources(use_cpu: bool, _register_file_assoc: bool) -> Result<Ap
190
  );
191
  }
192
 
193
- let ml_device = device_name(use_cpu);
194
- let ml = Arc::new(ml::Model::new(use_cpu).await?);
195
- let llm = Arc::new(llm::Model::new(use_cpu));
196
- let renderer = Arc::new(Renderer::new()?);
 
 
 
 
197
  let state = Arc::new(RwLock::new(State::default()));
198
 
199
  Ok(AppResources {
 
6
  },
7
  };
8
 
9
+ use anyhow::{Context, Result};
10
  use clap::Parser;
11
  use koharu_ml::{DeviceName, cuda_is_available, device_name};
12
  use koharu_runtime::{ensure_dylibs, preload_dylibs};
 
170
  Ok(())
171
  }
172
 
173
+ async fn build_resources(cpu: bool, _register_file_assoc: bool) -> Result<AppResources> {
174
  if cuda_is_available() {
175
+ ensure_dylibs(LIB_ROOT.to_path_buf())
176
+ .await
177
+ .context("Failed to ensure dynamic libraries")?;
178
+ preload_dylibs(LIB_ROOT.to_path_buf()).context("Failed to preload dynamic libraries")?;
179
 
180
  #[cfg(target_os = "windows")]
181
  {
 
183
  tracing::warn!(?err, "Failed to register .khr file association");
184
  }
185
 
186
+ crate::windows::add_dll_directory(&LIB_ROOT).context("Failed to add DLL directory")?;
187
  }
188
 
189
  tracing::info!(
 
192
  );
193
  }
194
 
195
+ let ml_device = device_name(cpu);
196
+ let ml = Arc::new(
197
+ ml::Model::new(cpu)
198
+ .await
199
+ .context("Failed to initialize ML model")?,
200
+ );
201
+ let llm = Arc::new(llm::Model::new(cpu));
202
+ let renderer = Arc::new(Renderer::new().context("Failed to initialize renderer")?);
203
  let state = Arc::new(RwLock::new(State::default()));
204
 
205
  Ok(AppResources {