1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Process-wide shared embedder singleton for the memory retrieval layer.
//!
//! Why: Extracted from retrieval/mod.rs to keep each file under the 500-SLOC
//! cap (#607). Owns the `SHARED_EMBEDDER` OnceCell and the helpers that
//! initialise or pre-seed it.
//! What: `SHARED_EMBEDDER` static, `shared_embedder()` async resolver,
//! `seed_shared_embedder_with_mock()` test helper.
//! Test: `shared_embedder_is_singleton` in retrieval::tests.
use crate;
use cratetimeouts;
use ;
use Arc;
use OnceCell;
/// Process-wide shared embedder (type-erased).
///
/// Why: `FastEmbedder::new()` loads a ~90 MB ONNX session — creating one per
/// call (as the previous `recall_with_default_embedder` / `remember` /
/// dream `dedup_pass` did) blew memory to multiple GB and forked dozens of
/// model instances. Issue #57. Typed as `dyn Embedder` so tests can seed the
/// cell with `MockEmbedder` before any ONNX download occurs (issue #850).
/// What: A `tokio::sync::OnceCell` initialised on first use and shared by every
/// caller that lacks a context-supplied embedder. Concurrent first-use races
/// collapse to a single load. Tests call `seed_shared_embedder_with_mock()`
/// before any `shared_embedder()` call to avoid HuggingFace downloads in CI.
/// Test: `shared_embedder_is_singleton` confirms two calls return the same
/// `Arc` pointer.
pub static SHARED_EMBEDDER: =
const_new;
/// Resolve (or initialise) the process-wide shared embedder.
///
/// Why: Centralising fallback embedder construction guarantees at most one
/// ONNX session per process — critical for the daemon footprint (issue #57).
/// Issue #906: `FastEmbedder::new()` can take 30-120 s on a CoreML cold-compile
/// or a CUDA warm-up — without a timeout the first `memory_remember` or
/// `memory_recall` call blocks indefinitely. Wrapping in
/// `tokio::time::timeout` turns the hang into an explicit error that the
/// caller (and the daemon) can surface to the user. Issue #2111: on some
/// Apple Silicon hosts the CoreML EP init inside `FastEmbedder::new()` does
/// not just run long — it hangs indefinitely with the OS thread blocked and
/// no cancellation point, so this `tokio::time::timeout` only abandons the
/// *future*; a naive retry from `get_or_try_init` on every subsequent caller
/// (e.g. each ~5-minute dream cycle) would otherwise leak one more
/// permanently blocked blocking-pool thread per retry. The fix lives one
/// layer down, in `FastEmbedder::try_new_bounded`
/// (`crate::embedder::fast_embedder`): it bounds the CoreML attempt itself
/// (default 60 s, well inside this function's 180 s default) and
/// auto-falls-back to CPU, remembering the hang process-wide via
/// `COREML_KNOWN_BAD` so CoreML is never attempted again this process. That
/// means `FastEmbedder::new()` now succeeds (on CPU) well within this
/// timeout in the common case — `get_or_try_init` here does not need to
/// retry at all — and even in the pathological case of a misconfigured
/// (too-short) outer timeout, at most one extra thread can leak before the
/// inner bound fires and poisons the cache, not an unbounded number.
/// What: Returns a clone of the shared `Arc<dyn Embedder + Send + Sync>`,
/// initialising it on first call via `FastEmbedder::new()`. The init is
/// bounded by `TRUSTY_EMBEDDER_INIT_TIMEOUT_SECS` (default 180 s). In test
/// builds, callers should first call `seed_shared_embedder_with_mock()` so
/// the cell is pre-populated with `MockEmbedder` and no model download is
/// attempted.
/// Test: `shared_embedder_is_singleton`; timeout path covered by
/// `timeout_wrapper_fires_on_embedder_init` in retrieval::tests; the
/// CoreML hang auto-recovery decision logic is covered by the
/// `decide_fallback` unit tests in `embedder::fast_embedder`.
pub async
/// Whether the process-wide shared embedder has been initialised.
///
/// Why (#4836): a daemon's readiness flag is a latch written by a single
/// startup warm-up attempt, so it can say `Warming` long after the embedder has
/// in fact initialised. Callers that degrade on that flag alone served a
/// query-independent recall fallback for the daemon's whole life. This lets such
/// a caller check the embedder itself before degrading — and it must not force
/// a cold init, or the check would reintroduce exactly the startup stall the
/// degraded path exists to avoid.
/// What: `OnceCell::initialized()` on `SHARED_EMBEDDER`. Cheap and lock-free;
/// monotonic (never returns `true` then `false`).
/// Test: `shared_embedder_initialized_flips_after_seeding`.
/// Pre-seed the shared embedder with a `MockEmbedder` for offline tests.
///
/// Why: CI environments cannot download the ~23 MB ONNX model from HuggingFace
/// without hitting HTTP 429 rate limits. Calling this before any `remember` /
/// `recall` / `dream_cycle` operation in tests avoids the download entirely by
/// pre-populating the process-wide `SHARED_EMBEDDER` cell with a deterministic
/// hash-based mock (issue #850 — mirrors the fix applied to trusty-agents in #813).
/// What: Attempts `OnceCell::set` with a 384-dim `MockEmbedder`. Idempotent
/// — if the cell was already set (by an earlier test in the same process), the
/// call is a silent no-op; the first caller wins.
/// Test: All memory-core tests that exercise the embedding path call this at
/// the start of their body; `shared_embedder_is_singleton` verifies ptr-eq.
/// Pre-seed the shared embedder with a caller-built embedder.
///
/// Why: #9275 — an integration test of the chunked pipeline needs a chunking
/// embedder behind `shared_embedder()`, which [`seed_shared_embedder_with_mock`]
/// cannot supply.
/// What: `OnceCell::set`; a no-op when the cell is already set (first caller
/// wins), exactly like the mock seeder.
/// Test: `needle_in_the_last_chunk_is_recalled_at_rank_1` seeds with it.