From 2f5dcda53c55e750e2b6de63fa15302f90f9b93f Mon Sep 17 00:00:00 2001 From: ajianaz Date: Wed, 21 Oct 2026 16:04:11 +0700 Subject: [PATCH] chore(deps): ort 2.0.0-rc.14 with an explicit Environment --- CHANGELOG.md | 1 + Cargo.lock | 8 ++++---- crates/uteke-core/Cargo.toml | 2 +- crates/uteke-core/src/embed/engine.rs | 5 ++++- crates/uteke-core/src/embed/ort_init.rs | 23 +++++++++++++++++++---- docs/install.md | 2 +- 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de81a0f9..338fdec7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ ### Changed +- Internal: `ort` 2.0.0-rc.12/rc.13 to rc.14 (replaces the dependabot PR, which cannot build): rc.14 builds sessions from an explicit `Environment`, so the one process-wide environment is now created in `embed::ort_init` and handed to `Session::builder`. The bundled ONNX Runtime stays at 1.24.4 (`api-18`). Checked with the real model: `remember` writes the embedding and `recall` finds it. - Internal: privacy filter modes and flag helpers, part 2b-1a of #1464 (`uteke_core::privacy`): `PrivacyMode` (`redact`, `strict`, `off`), `PrivacyConfig::prepare`, `Error::SecretDetected` and the `metadata.redacted`/`redactions` helpers. Not wired to a write path yet. - Internal: privacy filter token rules and PEM blocks, part 2b-1a of #1464 (`uteke_core::privacy`): Stripe, `whsec_`, Slack, Google, Basic and `Token`/`Bot` auth, `curl -u`, password flags and URL passwords, and PEM private key blocks in their escaped, CRLF, collapsed, quoted and string-literal forms. Scan windows only bound the work. The shared vectors move to version 3 (113 cases so far). - Internal: the privacy filter is wired into the core write paths, part 2b-1 of #1464: `remember`, `remember_with_contradiction`, room remember, `update_memory` and `import` mask secrets in the CONTENT before it is embedded, indexed or stored, so a masked secret is absent from the row, the FTS5 index, the timeline and the database file (checked by scanning the file bytes). `Uteke::set_privacy_config` takes `PrivacyMode` (`Redact`, the default, `Strict` or `Off`) and the allowlist; a masked memory gets `metadata.redacted = true` and `metadata.redactions` (`[{kind, count}]`, never a value), a `redacted` timeline event, and `RememberOutcome.redactions`. The flags follow the content: an update to clean content drops them, a masked round trip or a metadata-only update keeps them. `Strict` returns the new `Error::SecretDetected { kinds }` (an import skips that row). Detection was reworked after two adversarial reviews (on 330 ordinary texts the first version masked 139, on 147 leaks it missed 62; now 3 and 16): a bare `key`, `pass`, `token` or `secret` is an ordinary word and a name only when compound (`API_KEY`, `DB_PASS`, `apiKey`, `PGPASSWORD`) or written with `=`; a key or token value needs a digit, so type annotations and code references (`token: &str`, `config.jwtSecret`) are left alone; paths, ARNs, digests, `0x` addresses, versions and dates are not secrets; quoted values, URL query strings, glued pairs (`MODE=prod;API_KEY=...`), escaped JSON, table cells and CLI flags (`--password`, `docker login -p`, `curl -u`) are read whole; new kinds: Basic and `Token` authorization headers, passwords in URLs, Stripe, Slack, Google, npm and webhook secrets; a private key block needs its matching END (or is cut off) and key-like content. Known limits, next parts: metadata, tags and `source` are not scanned, the structural import (`uteke import` of a full export), room consolidation and the LLM extraction paths do not call the filter yet, a secret glued to letters or `_` (`MY_ghp_...`), a quoted passphrase with spaces and a value containing `(` can still pass, a field named `password` holding a hyphenated phrase is masked, and nothing reads `[privacy]` from `uteke.toml`, so until part 2b-2 the default `Redact` applies to every write. diff --git a/Cargo.lock b/Cargo.lock index 1ffcefff..e86d5be8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1541,9 +1541,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "ort" -version = "2.0.0-rc.13" +version = "2.0.0-rc.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4336a1e2b38848325241c72889086886004e589b7c74f335e60a8e8db5138a0b" +checksum = "08ca9287c05aace7e95250104a00d54b7da0fc9ac5f25efa424b80827d2e25eb" dependencies = [ "libloading", "ndarray", @@ -1554,9 +1554,9 @@ dependencies = [ [[package]] name = "ort-sys" -version = "2.0.0-rc.13" +version = "2.0.0-rc.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf211e3776eea6aec988552fa118dd746d70e1b1e5e244058d1c98015f3e5872" +checksum = "4af4763e553f916650b0bfe4b8d217d61c28607f6117e48b8ab5584d1138f22f" [[package]] name = "parking_lot" diff --git a/crates/uteke-core/Cargo.toml b/crates/uteke-core/Cargo.toml index 8ee749d3..4eb758af 100644 --- a/crates/uteke-core/Cargo.toml +++ b/crates/uteke-core/Cargo.toml @@ -47,7 +47,7 @@ fs2 = "0.4" # ONNX-only dependencies (gated behind `onnx` feature) # NOTE: load-dynamic requires api-18+ to avoid pykeio/ort#547 (vitis EP compile error). # download-binaries is intentionally removed — ORT lib is shipped as a sidecar instead. -ort = { version = "2.0.0-rc.12", default-features = false, features = ["load-dynamic", "ndarray", "api-18"], optional = true } +ort = { version = "2.0.0-rc.14", default-features = false, features = ["load-dynamic", "ndarray", "api-18"], optional = true } ndarray = { version = "0.17", optional = true } tokenizers = { version = "0.23", optional = true } # Non-optional: the embedding cache (all backends, incl. OpenAI/Ollama) diff --git a/crates/uteke-core/src/embed/engine.rs b/crates/uteke-core/src/embed/engine.rs index fb391e13..5e97b509 100644 --- a/crates/uteke-core/src/embed/engine.rs +++ b/crates/uteke-core/src/embed/engine.rs @@ -196,7 +196,10 @@ impl OnnxEmbedder { let num_threads = std::thread::available_parallelism() .map(|n| n.get()) .unwrap_or(4); - let session = ort::session::Session::builder() + let environment = ort_init::environment().ok_or_else(|| { + Error::embed_msg("ONNX Runtime environment is not initialized".to_string()) + })?; + let session = ort::session::Session::builder(environment) .map_err(|e| Error::embed("ONNX session builder", e))? .with_intra_threads(num_threads) .map_err(|e| Error::embed("ONNX intra_threads config", e))? diff --git a/crates/uteke-core/src/embed/ort_init.rs b/crates/uteke-core/src/embed/ort_init.rs index d7070da0..3eb6a24c 100644 --- a/crates/uteke-core/src/embed/ort_init.rs +++ b/crates/uteke-core/src/embed/ort_init.rs @@ -334,9 +334,21 @@ fn find_ort_in_dir(dir: &PathBuf) -> Option { None } +/// The process-wide ORT environment. Since `ort` 2.0.0-rc.14 a session is built +/// from an [`Environment`](ort::environment::Environment), and only one may +/// exist per process, so it lives here for the life of the process. +static ENVIRONMENT: std::sync::OnceLock = std::sync::OnceLock::new(); + +/// The environment created by [`init_ort_environment`], or `None` before it +/// has run (or when it failed). +pub fn environment() -> Option<&'static ort::environment::Environment> { + ENVIRONMENT.get() +} + /// Initialize the ORT environment using the resolved library path. /// -/// This must be called **once** before any `ort::Session` is created. +/// This must be called **once** before any `ort::Session` is created; the +/// environment it builds is then available from [`environment`]. /// Returns the `OrtLibInfo` on success so callers can log which variant is in use. /// /// # Errors @@ -347,15 +359,18 @@ fn find_ort_in_dir(dir: &PathBuf) -> Option { pub fn init_ort_environment() -> Result { let info = resolve_ort_lib()?; - ort::init_from(&info.lib_path) + let environment = ort::init_from(&info.lib_path) .map_err(|e| { format!( "Failed to load ONNX Runtime from '{}': {e}. \ - Ensure the library version matches ort crate 2.0.0-rc.12.", + Ensure the library version matches ort crate 2.0.0-rc.14.", info.lib_path.display() ) })? - .commit(); + .build() + .map_err(|e| format!("Failed to create the ONNX Runtime environment: {e}"))?; + // Only the first call can get here: a second `build` is refused by ort. + let _ = ENVIRONMENT.set(environment); tracing::info!( "ORT environment initialized successfully (AVX2={})", diff --git a/docs/install.md b/docs/install.md index 8f35661f..8861bf6f 100644 --- a/docs/install.md +++ b/docs/install.md @@ -153,6 +153,6 @@ The install script deploys three binaries plus the bundled ONNX Runtime shared l | `uteke` | Core CLI — remember, recall, search, list, etc. | | `uteke-serve` | HTTP server for remote access and MCP over HTTP | | `uteke-mcp` | Standalone MCP server for AI agent integration | -| `libonnxruntime.so*` (Linux) / `libonnxruntime*.dylib` (macOS) / `onnxruntime.dll` (Windows) | ONNX Runtime (currently v1.24.4, matches `ort` crate `2.0.0-rc.12`) — resolved via `/libonnxruntime.so`, system paths, or `ORT_LIB_PATH` | +| `libonnxruntime.so*` (Linux) / `libonnxruntime*.dylib` (macOS) / `onnxruntime.dll` (Windows) | ONNX Runtime (currently v1.24.4, matches `ort` crate `2.0.0-rc.14`) — resolved via `/libonnxruntime.so`, system paths, or `ORT_LIB_PATH` | > **Arch / CachyOS note:** there is no system `libonnxruntime.so` by default and `pip install onnxruntime` may have no wheel for newer Python (e.g. 3.14), so you must keep the bundled `.so` next to the binary in `~/.local/bin/`. If you see `ONNX Runtime library not found` on `uteke remember`, re-run the installer above or set `ORT_LIB_PATH=/path/to/libonnxruntime.so`. Then `uteke doctor` should show `Embedding model: embeddinggemma-q4`.