Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/uteke-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion crates/uteke-core/src/embed/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))?
Expand Down
23 changes: 19 additions & 4 deletions crates/uteke-core/src/embed/ort_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,21 @@ fn find_ort_in_dir(dir: &PathBuf) -> Option<PathBuf> {
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<ort::environment::Environment> = 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
Expand All @@ -347,15 +359,18 @@ fn find_ort_in_dir(dir: &PathBuf) -> Option<PathBuf> {
pub fn init_ort_environment() -> Result<OrtLibInfo, String> {
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={})",
Expand Down
2 changes: 1 addition & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<exe_dir>/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 `<exe_dir>/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`.
Loading