mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
feat(learning): surface facet provenance (cue_families, evidence_refs) in RPC (#5043)
This commit is contained in:
@@ -62,7 +62,7 @@ Namespace `learning` (wired into `src/core/all.rs`; 11 controllers). Methods:
|
||||
| `learning.rebuild_cache` | Manually trigger a `StabilityDetector` rebuild; returns added/evicted/kept/total_size. |
|
||||
| `learning.cache_stats` | Cache totals + per-state and per-class breakdown. |
|
||||
| `learning.list_facets` | List Active + Provisional facets, optional `class` filter. |
|
||||
| `learning.get_facet` | Fetch one facet by `class` + `key` suffix. |
|
||||
| `learning.get_facet` | Fetch one facet by `class` + `key` suffix. Each returned facet carries its provenance (`evidence_refs`, `cue_families`) alongside the value/state fields. |
|
||||
| `learning.update_facet` | Set a facet value and pin it (`user_state = Pinned`). |
|
||||
| `learning.pin_facet` / `learning.unpin_facet` | Toggle `user_state` Pinned ↔ Auto. |
|
||||
| `learning.forget_facet` | Mark `Dropped` + `user_state = Forgotten` (blocks re-promotion). |
|
||||
|
||||
@@ -475,6 +475,54 @@ mod tests {
|
||||
assert_eq!(s.function, "unknown");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn facet_to_json_includes_cue_families_and_evidence_refs() {
|
||||
use crate::openhuman::learning::candidate::EvidenceRef;
|
||||
use crate::openhuman::memory_store::profile::{
|
||||
FacetState, FacetType, ProfileFacet, UserState,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
let mut cue_families = HashMap::new();
|
||||
cue_families.insert("explicit".to_string(), 3u32);
|
||||
cue_families.insert("structural".to_string(), 1u32);
|
||||
|
||||
let facet = ProfileFacet {
|
||||
facet_id: "f1".into(),
|
||||
facet_type: FacetType::Preference,
|
||||
key: "style/verbosity".into(),
|
||||
value: "terse".into(),
|
||||
confidence: 0.8,
|
||||
evidence_count: 4,
|
||||
source_segment_ids: None,
|
||||
first_seen_at: 1000.0,
|
||||
last_seen_at: 1200.0,
|
||||
state: FacetState::Active,
|
||||
stability: 0.9,
|
||||
user_state: UserState::Auto,
|
||||
evidence_refs: vec![EvidenceRef::Episodic { episodic_id: 42 }],
|
||||
class: Some("style".into()),
|
||||
cue_families: Some(cue_families),
|
||||
};
|
||||
|
||||
// Populated provenance round-trips through the serializer.
|
||||
let json = facet_to_json(&facet);
|
||||
assert_eq!(json["cue_families"]["explicit"].as_u64(), Some(3));
|
||||
assert_eq!(json["cue_families"]["structural"].as_u64(), Some(1));
|
||||
assert_eq!(json["evidence_refs"][0]["type"].as_str(), Some("episodic"));
|
||||
assert_eq!(json["evidence_refs"][0]["episodic_id"].as_i64(), Some(42));
|
||||
|
||||
// Empty/None provenance serializes to []/null (present, not dropped).
|
||||
let bare = ProfileFacet {
|
||||
evidence_refs: vec![],
|
||||
cue_families: None,
|
||||
..facet
|
||||
};
|
||||
let json = facet_to_json(&bare);
|
||||
assert_eq!(json["evidence_refs"].as_array().map(Vec::len), Some(0));
|
||||
assert!(json["cue_families"].is_null());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn schemas_and_controllers_match() {
|
||||
let s = all_learning_controller_schemas();
|
||||
@@ -735,6 +783,12 @@ fn facet_to_json(f: &crate::openhuman::memory_store::profile::ProfileFacet) -> s
|
||||
"first_seen_at": f.first_seen_at,
|
||||
"last_seen_at": f.last_seen_at,
|
||||
"class": f.class,
|
||||
// Provenance the store already persists and row_to_facet hydrates, but
|
||||
// the serializer previously dropped: the citations behind the facet and
|
||||
// the per-cue-family evidence counts. `None`/empty serialize to
|
||||
// `null`/`[]`.
|
||||
"cue_families": f.cue_families,
|
||||
"evidence_refs": f.evidence_refs,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user