diff --git a/CHANGELOG.md b/CHANGELOG.md index f403cc6..782dcd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.11.102] - 2026-07-16 + +### Added + +- **`RecalledMemory` gains optional `vector_score` and `text_score` fields** — Server v0.11.98 + now surfaces hybrid sub-scores (vector similarity component and BM25 text component) alongside + `smart_score` on every recall result. Both fields are `Option` and absent when the server + did not emit them (e.g. vector-only or BM25-only queries). Additive — no ranking change. + ([server PR#767](https://github.com/Dakera-AI/dakera/pull/767)) + +### Changed + +- **Server sync v0.11.98** — syncs to server v0.11.98: WAL durability fix (data-loss off-by-one + LSN guard), unified-query pagination cursor now correct, bi-temporal `valid_from` field + now persisted, candle 0.10→0.11 upgrade, adaptive w_vec boost (CE-147), RocksDB opt-in + persistent hot tier, Phase-1 speed wave (7 latency fixes), graph handlers on `spawn_blocking`. + ## [0.11.101] - 2026-07-05 ### Changed diff --git a/Cargo.toml b/Cargo.toml index e470e68..580d8f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dakera-client" -version = "0.11.101" +version = "0.11.102" edition = "2021" description = "Rust client SDK for Dakera AI Agent Memory Platform" license = "MIT" diff --git a/src/memory.rs b/src/memory.rs index 6d3cc38..445d128 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -462,6 +462,12 @@ pub struct RecalledMemory { /// KG-3: hop depth at which this memory was found (only set on associated memories) #[serde(skip_serializing_if = "Option::is_none")] pub depth: Option, + /// Hybrid sub-score: vector similarity component (server v0.11.98+, absent when BM25-only) + #[serde(skip_serializing_if = "Option::is_none")] + pub vector_score: Option, + /// Hybrid sub-score: BM25 text component (server v0.11.98+, absent when vector-only) + #[serde(skip_serializing_if = "Option::is_none")] + pub text_score: Option, } impl<'de> serde::Deserialize<'de> for RecalledMemory { @@ -471,8 +477,8 @@ impl<'de> serde::Deserialize<'de> for RecalledMemory { use serde::de::Error as _; let val = serde_json::Value::deserialize(deserializer)?; - // Server wraps recall results as {memory:{...}, score, weighted_score, smart_score}. - // smart_score is the actual ranking key (server sorts by it); prefer it. + // Server wraps recall results as {memory:{...}, score, weighted_score, smart_score, + // vector_score, text_score}. smart_score is the actual ranking key (server sorts by it). // Fall back to flat format for direct memory-get responses. let smart_score = val .get("smart_score") @@ -486,6 +492,15 @@ impl<'de> serde::Deserialize<'de> for RecalledMemory { .or(weighted_score) .or_else(|| val.get("score").and_then(|v| v.as_f64()).map(|v| v as f32)) .unwrap_or(0.0); + // Hybrid sub-scores (server v0.11.98+): surfaced at the top level alongside smart_score. + let vector_score = val + .get("vector_score") + .and_then(|v| v.as_f64()) + .map(|v| v as f32); + let text_score = val + .get("text_score") + .and_then(|v| v.as_f64()) + .map(|v| v as f32); let mem = val.get("memory").unwrap_or(&val); @@ -542,6 +557,8 @@ impl<'de> serde::Deserialize<'de> for RecalledMemory { last_accessed_at, access_count, depth, + vector_score, + text_score, }) } }