From be020bce5ab2aba1bc8d9ccc0ed2d57767f8749c Mon Sep 17 00:00:00 2001 From: Ofer Shaal Date: Tue, 21 Apr 2026 11:42:30 -0400 Subject: [PATCH] fix(sona): expose find_patterns on EphemeralAgent + fix get_patterns returning empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `EphemeralAgent::get_patterns()` was calling `self.engine.find_patterns(&[], 0)` — an empty-query / k=0 lookup that always returns `Vec::new()`. The shape of the call makes it clear this was stub scaffolding rather than an intended behaviour. Consumers calling `get_patterns()` silently received an empty list no matter how many patterns the agent had accumulated. The underlying `engine.find_patterns(query, k)` works correctly; the miss was that it was never exposed as a public method on `EphemeralAgent`, so callers had no way to actually query the reasoning bank for a top-k by similarity. Changes (40 lines across two files): 1. crates/sona/src/training/federated.rs - `get_patterns()` now delegates to `self.engine.get_all_patterns()` and returns the full learned-pattern list. - New public method `find_patterns(query: &[f32], k: usize)` forwards to `self.engine.find_patterns(query, k)` so callers can do a cosine-ranked top-k from a query embedding. 2. crates/sona/src/wasm.rs - New `#[wasm_bindgen(js_name = findPatterns)]` binding on `WasmEphemeralAgent` that accepts a Float32Array query vector and a k, and returns the serialized `Vec` via serde_wasm_bindgen (matches the existing `getPatterns` pattern). No behavioural change for existing callers of `getPatterns` beyond it now returning the non-empty list it was presumably always meant to return. Verified by vendoring the patched crate into a downstream consumer and confirming `findPatterns(embedding, k)` round-trips a populated array with the expected LearnedPattern shape; `getPatterns()` also now returns a non-empty list when the agent has accumulated patterns. --- crates/sona/src/training/federated.rs | 7 ++++++- crates/sona/src/wasm.rs | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/crates/sona/src/training/federated.rs b/crates/sona/src/training/federated.rs index eaf76d053..a95943f18 100644 --- a/crates/sona/src/training/federated.rs +++ b/crates/sona/src/training/federated.rs @@ -232,7 +232,12 @@ impl EphemeralAgent { /// Get learned patterns from agent pub fn get_patterns(&self) -> Vec { - self.engine.find_patterns(&[], 0) + self.engine.get_all_patterns() + } + + /// Find top-k patterns most similar to a query embedding + pub fn find_patterns(&self, query: &[f32], k: usize) -> Vec { + self.engine.find_patterns(query, k) } /// Export agent state for federation diff --git a/crates/sona/src/wasm.rs b/crates/sona/src/wasm.rs index 398561d48..d66584291 100644 --- a/crates/sona/src/wasm.rs +++ b/crates/sona/src/wasm.rs @@ -523,6 +523,17 @@ impl WasmEphemeralAgent { let patterns = self.inner.get_patterns(); serde_wasm_bindgen::to_value(&patterns).unwrap_or(JsValue::NULL) } + + /// Find top-k patterns most similar to a query embedding + /// + /// # Arguments + /// * `query_embedding` - Query vector as Float32Array + /// * `k` - Number of patterns to return + #[wasm_bindgen(js_name = findPatterns)] + pub fn find_patterns(&self, query_embedding: Vec, k: usize) -> JsValue { + let patterns = self.inner.find_patterns(&query_embedding, k); + serde_wasm_bindgen::to_value(&patterns).unwrap_or(JsValue::NULL) + } } /// WASM-compatible Federated Coordinator