use super::*;
use crate::shared::api::EmbedRole;
pub(crate) fn self_notes_recent(
storage: &crate::shared::storage::Storage,
profile_id: Uuid,
limit: usize,
) -> Vec<Note> {
let mut notes = storage
.db()
.note_list(profile_id, None, &[SELF_NOTE_TAG.to_string()], None)
.unwrap_or_default();
notes.truncate(limit);
notes
}
pub(crate) async fn self_notes_relevant(
storage: &crate::shared::storage::Storage,
embedder: &dyn crate::shared::api::Embedder,
profile_id: Uuid,
query: &str,
limit: usize,
) -> Vec<Note> {
if query.trim().is_empty() || limit == 0 {
return Vec::new();
}
ensure_note_vectors(storage, embedder, profile_id).await;
let Ok(vecs) = embedder
.embed(vec![query.to_string()], EmbedRole::Query)
.await
else {
return Vec::new();
};
let Some(emb) = vecs.into_iter().next() else {
return Vec::new();
};
let Ok(hits) = storage
.db()
.note_search_semantic(profile_id, &emb, limit.max(20))
else {
return Vec::new();
};
hits.into_iter()
.map(|(n, _)| n)
.filter(is_self_note)
.take(limit)
.collect()
}
pub(crate) fn self_related_block(ctx: &ToolContext, shown: &[Uuid]) -> Option<String> {
let shown_set: std::collections::HashSet<Uuid> = shown.iter().copied().collect();
let mut seen_edges: std::collections::HashSet<(Uuid, Uuid, String)> =
std::collections::HashSet::new();
let mut lines: Vec<String> = Vec::new();
for id in shown {
let nb = ctx
.storage
.db()
.note_neighbors(ctx.profile_id, *id, None)
.unwrap_or_default();
for (note, relation, outgoing) in nb {
let (from, to) = edge_key(*id, note.id, outgoing);
if !seen_edges.insert((from, to, relation.clone())) {
continue;
}
lines.push(self_edge_line(
ctx, &shown_set, *id, ¬e, &relation, outgoing,
));
if lines.len() >= RELATED_IN_RECALL {
break;
}
}
if lines.len() >= RELATED_IN_RECALL {
break;
}
}
if lines.is_empty() {
None
} else {
Some(format!(
"\n{}:\n{}",
ctx.loc.t("notes.block.self_related"),
lines.join("\n")
))
}
}
fn edge_key(id: Uuid, neighbor: Uuid, outgoing: bool) -> (Uuid, Uuid) {
if outgoing {
(id, neighbor)
} else {
(neighbor, id)
}
}
fn self_edge_line(
ctx: &ToolContext,
shown_set: &std::collections::HashSet<Uuid>,
id: Uuid,
note: &Note,
relation: &str,
outgoing: bool,
) -> String {
let mark = if is_self_note(note) {
String::new()
} else {
format!("{} ", ctx.loc.t("notes.mark.note"))
};
let tail = if shown_set.contains(¬e.id) {
format!("(id={})", note.id)
} else {
format!("{mark}(id={}) {}", note.id, note.content)
};
let arrow = if outgoing { "→" } else { "←" };
format!("- (id={id}) {arrow}{relation} {tail}")
}
pub(crate) fn migrate_self_narrative(storage: &crate::shared::storage::Storage, profile_id: Uuid) {
use crate::entities::self_model::NarrativeSegment;
let mut segments: Vec<NarrativeSegment> = Vec::new();
let _ = storage.db().self_model_update(profile_id, |m| {
if m.narrative.is_empty() {
return false;
}
segments = std::mem::take(&mut m.narrative);
true
});
for seg in segments {
let note = Note {
id: Uuid::new_v4(),
profile_id,
content: seg.text,
tags: vec![SELF_NOTE_TAG.to_string()],
created_at: seg.created_at,
updated_at: seg.created_at,
};
let _ = storage.db().note_insert(¬e);
}
}