extract truncation helpers, fix clippy warnings, dedup batching loop
Add util::truncate() and util::first_n_chars() to replace 16 call sites doing the same floor_char_boundary or chars().take().collect() patterns. Deduplicate the batching loop in consolidate.rs (4 copies → 1 loop over an array). Fix all clippy warnings: redundant closures, needless borrows, collapsible if, unnecessary cast, manual strip_prefix. Net: -44 lines across 16 files.
This commit is contained in:
parent
e24dee6bdf
commit
52523403c5
16 changed files with 85 additions and 129 deletions
|
|
@ -66,40 +66,18 @@ pub fn consolidate_full_with_progress(
|
||||||
if plan.run_health {
|
if plan.run_health {
|
||||||
runs.push(("health", 0));
|
runs.push(("health", 0));
|
||||||
}
|
}
|
||||||
if plan.replay_count > 0 {
|
let batch_size = 5;
|
||||||
let batch = 5;
|
for (name, count) in [
|
||||||
let mut remaining = plan.replay_count;
|
("replay", plan.replay_count),
|
||||||
|
("linker", plan.linker_count),
|
||||||
|
("separator", plan.separator_count),
|
||||||
|
("transfer", plan.transfer_count),
|
||||||
|
] {
|
||||||
|
let mut remaining = count;
|
||||||
while remaining > 0 {
|
while remaining > 0 {
|
||||||
let this_batch = remaining.min(batch);
|
let batch = remaining.min(batch_size);
|
||||||
runs.push(("replay", this_batch));
|
runs.push((name, batch));
|
||||||
remaining -= this_batch;
|
remaining -= batch;
|
||||||
}
|
|
||||||
}
|
|
||||||
if plan.linker_count > 0 {
|
|
||||||
let batch = 5;
|
|
||||||
let mut remaining = plan.linker_count;
|
|
||||||
while remaining > 0 {
|
|
||||||
let this_batch = remaining.min(batch);
|
|
||||||
runs.push(("linker", this_batch));
|
|
||||||
remaining -= this_batch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if plan.separator_count > 0 {
|
|
||||||
let batch = 5;
|
|
||||||
let mut remaining = plan.separator_count;
|
|
||||||
while remaining > 0 {
|
|
||||||
let this_batch = remaining.min(batch);
|
|
||||||
runs.push(("separator", this_batch));
|
|
||||||
remaining -= this_batch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if plan.transfer_count > 0 {
|
|
||||||
let batch = 5;
|
|
||||||
let mut remaining = plan.transfer_count;
|
|
||||||
while remaining > 0 {
|
|
||||||
let this_batch = remaining.min(batch);
|
|
||||||
runs.push(("transfer", this_batch));
|
|
||||||
remaining -= this_batch;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ fn job_experience_mine(ctx: &ExecutionContext, path: &str, segment: Option<usize
|
||||||
let mut store = crate::store::Store::load()?;
|
let mut store = crate::store::Store::load()?;
|
||||||
ctx.log_line("mining");
|
ctx.log_line("mining");
|
||||||
let count = crate::enrich::experience_mine(&mut store, &path, segment)?;
|
let count = crate::enrich::experience_mine(&mut store, &path, segment)?;
|
||||||
ctx.log_line(&format!("{} entries mined", count));
|
ctx.log_line(format!("{count} entries mined"));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -116,7 +116,7 @@ fn job_fact_mine(ctx: &ExecutionContext, path: &str) -> Result<(), TaskError> {
|
||||||
let p = std::path::Path::new(&path);
|
let p = std::path::Path::new(&path);
|
||||||
let progress = |msg: &str| { ctx.set_progress(msg); };
|
let progress = |msg: &str| { ctx.set_progress(msg); };
|
||||||
let count = crate::fact_mine::mine_and_store(p, Some(&progress))?;
|
let count = crate::fact_mine::mine_and_store(p, Some(&progress))?;
|
||||||
ctx.log_line(&format!("{} facts stored", count));
|
ctx.log_line(format!("{count} facts stored"));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +140,7 @@ fn job_knowledge_loop(ctx: &ExecutionContext) -> Result<(), TaskError> {
|
||||||
};
|
};
|
||||||
ctx.log_line("running agents");
|
ctx.log_line("running agents");
|
||||||
let results = crate::knowledge::run_knowledge_loop(&config)?;
|
let results = crate::knowledge::run_knowledge_loop(&config)?;
|
||||||
ctx.log_line(&format!("{} cycles, {} actions",
|
ctx.log_line(format!("{} cycles, {} actions",
|
||||||
results.len(),
|
results.len(),
|
||||||
results.iter().map(|r| r.total_applied).sum::<usize>()));
|
results.iter().map(|r| r.total_applied).sum::<usize>()));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -505,7 +505,7 @@ pub fn run_daemon() -> Result<(), String> {
|
||||||
if extract_pending > 0 { parts.push(format!("{} extract", extract_pending)); }
|
if extract_pending > 0 { parts.push(format!("{} extract", extract_pending)); }
|
||||||
if fact_pending > 0 { parts.push(format!("{} fact", fact_pending)); }
|
if fact_pending > 0 { parts.push(format!("{} fact", fact_pending)); }
|
||||||
if still_open > 0 { parts.push(format!("{} open", still_open)); }
|
if still_open > 0 { parts.push(format!("{} open", still_open)); }
|
||||||
ctx.set_progress(&parts.join(", "));
|
ctx.set_progress(parts.join(", "));
|
||||||
} else {
|
} else {
|
||||||
ctx.set_progress("idle");
|
ctx.set_progress("idle");
|
||||||
}
|
}
|
||||||
|
|
@ -575,7 +575,7 @@ pub fn run_daemon() -> Result<(), String> {
|
||||||
digest.depend_on(&knowledge);
|
digest.depend_on(&knowledge);
|
||||||
|
|
||||||
*last_daily_sched.lock().unwrap() = Some(today);
|
*last_daily_sched.lock().unwrap() = Some(today);
|
||||||
ctx.set_progress(&format!("daily pipeline triggered ({})", today));
|
ctx.set_progress(format!("daily pipeline triggered ({today})"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune finished tasks from registry
|
// Prune finished tasks from registry
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use std::collections::BTreeSet;
|
||||||
|
|
||||||
// --- Digest level descriptors ---
|
// --- Digest level descriptors ---
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
struct DigestLevel {
|
struct DigestLevel {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
title: &'static str,
|
title: &'static str,
|
||||||
|
|
|
||||||
|
|
@ -165,11 +165,7 @@ pub fn split_on_compaction(messages: Vec<(usize, String, String, String)>) -> Ve
|
||||||
fn format_conversation(messages: &[(usize, String, String, String)]) -> String {
|
fn format_conversation(messages: &[(usize, String, String, String)]) -> String {
|
||||||
messages.iter()
|
messages.iter()
|
||||||
.map(|(line, role, text, ts)| {
|
.map(|(line, role, text, ts)| {
|
||||||
let text = if text.len() > 2000 {
|
let text = crate::util::truncate(text, 1800, "...[truncated]");
|
||||||
format!("{}...[truncated]", &text[..text.floor_char_boundary(1800)])
|
|
||||||
} else {
|
|
||||||
text.clone()
|
|
||||||
};
|
|
||||||
if ts.is_empty() {
|
if ts.is_empty() {
|
||||||
format!("L{} [{}]: {}", line, role, text)
|
format!("L{} [{}]: {}", line, role, text)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -424,13 +420,8 @@ pub fn experience_mine(
|
||||||
let _ = store.upsert_node(node);
|
let _ = store.upsert_node(node);
|
||||||
count += 1;
|
count += 1;
|
||||||
|
|
||||||
let preview = if content.len() > 80 {
|
let preview = crate::util::truncate(content, 77, "...");
|
||||||
let end = content.floor_char_boundary(77);
|
println!(" + [{}] {}", ts, preview);
|
||||||
&content[..end]
|
|
||||||
} else {
|
|
||||||
content
|
|
||||||
};
|
|
||||||
println!(" + [{}] {}...", ts, preview);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record this transcript/segment as mined (even if count == 0, to prevent re-runs)
|
// Record this transcript/segment as mined (even if count == 0, to prevent re-runs)
|
||||||
|
|
|
||||||
|
|
@ -144,13 +144,7 @@ fn extract_conversation(path: &Path) -> Vec<Message> {
|
||||||
fn format_for_extraction(messages: &[Message]) -> String {
|
fn format_for_extraction(messages: &[Message]) -> String {
|
||||||
messages.iter()
|
messages.iter()
|
||||||
.map(|msg| {
|
.map(|msg| {
|
||||||
let text = if msg.text.len() > 3000 {
|
let text = crate::util::truncate(&msg.text, 2800, "\n[...truncated...]");
|
||||||
// Find a char boundary near 2800
|
|
||||||
let trunc = msg.text.floor_char_boundary(2800);
|
|
||||||
format!("{}\n[...truncated...]", &msg.text[..trunc])
|
|
||||||
} else {
|
|
||||||
msg.text.clone()
|
|
||||||
};
|
|
||||||
let ts = if msg.timestamp.len() >= 19 { &msg.timestamp[..19] } else { "" };
|
let ts = if msg.timestamp.len() >= 19 { &msg.timestamp[..19] } else { "" };
|
||||||
if ts.is_empty() {
|
if ts.is_empty() {
|
||||||
format!("[{}] {}", msg.role, text)
|
format!("[{}] {}", msg.role, text)
|
||||||
|
|
@ -244,8 +238,7 @@ pub fn mine_transcript(
|
||||||
if dry_run {
|
if dry_run {
|
||||||
for (i, (offset, chunk)) in chunks.iter().enumerate() {
|
for (i, (offset, chunk)) in chunks.iter().enumerate() {
|
||||||
eprintln!("\n--- Chunk {} (offset {}, {} chars) ---", i + 1, offset, chunk.len());
|
eprintln!("\n--- Chunk {} (offset {}, {} chars) ---", i + 1, offset, chunk.len());
|
||||||
let preview = if chunk.len() > 500 { &chunk[..chunk.floor_char_boundary(500)] } else { chunk };
|
eprintln!("{}", crate::util::truncate(chunk, 500, ""));
|
||||||
eprintln!("{}", preview);
|
|
||||||
if chunk.len() > 500 {
|
if chunk.len() > 500 {
|
||||||
eprintln!(" ... ({} more chars)", chunk.len() - 500);
|
eprintln!(" ... ({} more chars)", chunk.len() - 500);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -631,8 +631,8 @@ pub fn run_challenger(store: &Store, graph: &Graph, batch_size: usize) -> Result
|
||||||
let template = load_prompt("challenger")?;
|
let template = load_prompt("challenger")?;
|
||||||
let topology = get_graph_topology(store, graph);
|
let topology = get_graph_topology(store, graph);
|
||||||
|
|
||||||
let mut candidates: Vec<(&String, usize)> = store.nodes.iter()
|
let mut candidates: Vec<(&String, usize)> = store.nodes.keys()
|
||||||
.map(|(k, _)| (k, graph.degree(k)))
|
.map(|k| (k, graph.degree(k)))
|
||||||
.collect();
|
.collect();
|
||||||
candidates.sort_by(|a, b| b.1.cmp(&a.1));
|
candidates.sort_by(|a, b| b.1.cmp(&a.1));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ fn call_model(agent: &str, model: &str, prompt: &str) -> Result<String, String>
|
||||||
Ok(response)
|
Ok(response)
|
||||||
} else {
|
} else {
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
let preview: String = stderr.chars().take(500).collect();
|
let preview = crate::util::first_n_chars(&stderr, 500);
|
||||||
log_usage(agent, model, prompt, &preview, elapsed, false);
|
log_usage(agent, model, prompt, &preview, elapsed, false);
|
||||||
Err(format!("claude exited {}: {}", output.status, preview.trim()))
|
Err(format!("claude exited {}: {}", output.status, preview.trim()))
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +129,7 @@ pub(crate) fn parse_json_response(response: &str) -> Result<serde_json::Value, S
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let preview: String = cleaned.chars().take(200).collect();
|
let preview = crate::util::first_n_chars(cleaned, 200);
|
||||||
Err(format!("no valid JSON in response: {preview}..."))
|
Err(format!("no valid JSON in response: {preview}..."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -637,10 +637,11 @@ fn cmd_search(terms: &[String], expand: bool) -> Result<(), String> {
|
||||||
let weight = view.node_weight(k);
|
let weight = view.node_weight(k);
|
||||||
println!(" ~ [{:.2}] {}", weight, k);
|
println!(" ~ [{:.2}] {}", weight, k);
|
||||||
if let Some(content) = view.node_content(k) {
|
if let Some(content) = view.node_content(k) {
|
||||||
let snippet: String = content.lines()
|
let snippet = util::first_n_chars(
|
||||||
.find(|l| !l.trim().is_empty() && !l.starts_with('#'))
|
content.lines()
|
||||||
.unwrap_or("")
|
.find(|l| !l.trim().is_empty() && !l.starts_with('#'))
|
||||||
.chars().take(100).collect();
|
.unwrap_or(""),
|
||||||
|
100);
|
||||||
if !snippet.is_empty() {
|
if !snippet.is_empty() {
|
||||||
println!(" {}", snippet);
|
println!(" {}", snippet);
|
||||||
}
|
}
|
||||||
|
|
@ -671,13 +672,11 @@ fn cmd_init() -> Result<(), String> {
|
||||||
let mut store = store::Store::load()?;
|
let mut store = store::Store::load()?;
|
||||||
let count = store.init_from_markdown()?;
|
let count = store.init_from_markdown()?;
|
||||||
for key in &cfg.core_nodes {
|
for key in &cfg.core_nodes {
|
||||||
if !store.nodes.contains_key(key.as_str()) {
|
if !store.nodes.contains_key(key) && key == "identity" {
|
||||||
if key == "identity" {
|
let default = include_str!("../defaults/identity.md");
|
||||||
let default = include_str!("../defaults/identity.md");
|
store.upsert(key, default)
|
||||||
store.upsert(key, default)
|
.map_err(|e| format!("seed {}: {}", key, e))?;
|
||||||
.map_err(|e| format!("seed {}: {}", key, e))?;
|
println!("Seeded {} in store", key);
|
||||||
println!("Seeded {} in store", key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
store.save()?;
|
store.save()?;
|
||||||
|
|
@ -1319,12 +1318,7 @@ fn cmd_trace(key: &[String]) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show content preview
|
// Show content preview
|
||||||
let preview = if node.content.len() > 200 {
|
let preview = util::truncate(&node.content, 200, "...");
|
||||||
let end = node.content.floor_char_boundary(200);
|
|
||||||
format!("{}...", &node.content[..end])
|
|
||||||
} else {
|
|
||||||
node.content.clone()
|
|
||||||
};
|
|
||||||
println!("\n{}\n", preview);
|
println!("\n{}\n", preview);
|
||||||
|
|
||||||
// Walk neighbors, grouped by node type
|
// Walk neighbors, grouped by node type
|
||||||
|
|
@ -1354,7 +1348,7 @@ fn cmd_trace(key: &[String]) -> Result<(), String> {
|
||||||
if !episodic_weekly.is_empty() {
|
if !episodic_weekly.is_empty() {
|
||||||
println!("Weekly digests:");
|
println!("Weekly digests:");
|
||||||
for (k, s, n) in &episodic_weekly {
|
for (k, s, n) in &episodic_weekly {
|
||||||
let preview = n.content.lines().next().unwrap_or("").chars().take(80).collect::<String>();
|
let preview = util::first_n_chars(n.content.lines().next().unwrap_or(""), 80);
|
||||||
println!(" [{:.2}] {} — {}", s, k, preview);
|
println!(" [{:.2}] {} — {}", s, k, preview);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1362,7 +1356,7 @@ fn cmd_trace(key: &[String]) -> Result<(), String> {
|
||||||
if !episodic_daily.is_empty() {
|
if !episodic_daily.is_empty() {
|
||||||
println!("Daily digests:");
|
println!("Daily digests:");
|
||||||
for (k, s, n) in &episodic_daily {
|
for (k, s, n) in &episodic_daily {
|
||||||
let preview = n.content.lines().next().unwrap_or("").chars().take(80).collect::<String>();
|
let preview = util::first_n_chars(n.content.lines().next().unwrap_or(""), 80);
|
||||||
println!(" [{:.2}] {} — {}", s, k, preview);
|
println!(" [{:.2}] {} — {}", s, k, preview);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1370,9 +1364,11 @@ fn cmd_trace(key: &[String]) -> Result<(), String> {
|
||||||
if !episodic_session.is_empty() {
|
if !episodic_session.is_empty() {
|
||||||
println!("Session entries:");
|
println!("Session entries:");
|
||||||
for (k, s, n) in &episodic_session {
|
for (k, s, n) in &episodic_session {
|
||||||
let preview = n.content.lines()
|
let preview = util::first_n_chars(
|
||||||
.find(|l| !l.is_empty() && !l.starts_with("<!--"))
|
n.content.lines()
|
||||||
.unwrap_or("").chars().take(80).collect::<String>();
|
.find(|l| !l.is_empty() && !l.starts_with("<!--"))
|
||||||
|
.unwrap_or(""),
|
||||||
|
80);
|
||||||
println!(" [{:.2}] {}", s, k);
|
println!(" [{:.2}] {}", s, k);
|
||||||
if !n.source_ref.is_empty() {
|
if !n.source_ref.is_empty() {
|
||||||
println!(" ↳ source: {}", n.source_ref);
|
println!(" ↳ source: {}", n.source_ref);
|
||||||
|
|
@ -1625,7 +1621,7 @@ fn cmd_journal_ts_migrate() -> Result<(), String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(node) = store.nodes.get_mut(key) {
|
if let Some(node) = store.nodes.get_mut(key) {
|
||||||
node.created_at = node.timestamp as i64;
|
node.created_at = node.timestamp;
|
||||||
node.version += 1;
|
node.version += 1;
|
||||||
updated += 1;
|
updated += 1;
|
||||||
}
|
}
|
||||||
|
|
@ -1811,7 +1807,7 @@ fn cmd_history(key: &[String], full: bool) -> Result<(), String> {
|
||||||
node.version, ts, node.provenance.label(), node.weight, content_len);
|
node.version, ts, node.provenance.label(), node.weight, content_len);
|
||||||
eprintln!("{}", node.content);
|
eprintln!("{}", node.content);
|
||||||
} else {
|
} else {
|
||||||
let preview: String = node.content.chars().take(120).collect();
|
let preview = util::first_n_chars(&node.content, 120);
|
||||||
let preview = preview.replace('\n', "\\n");
|
let preview = preview.replace('\n', "\\n");
|
||||||
eprintln!(" v{:<3} {} {:24} w={:.3} {}b",
|
eprintln!(" v{:<3} {} {:24} w={:.3} {}b",
|
||||||
node.version, ts, node.provenance.label(), node.weight, content_len);
|
node.version, ts, node.provenance.label(), node.weight, content_len);
|
||||||
|
|
@ -2065,18 +2061,12 @@ fn extract_title(content: &str) -> String {
|
||||||
let stripped = line.trim();
|
let stripped = line.trim();
|
||||||
if stripped.is_empty() { continue; }
|
if stripped.is_empty() { continue; }
|
||||||
if date_re.is_match(stripped) && stripped.len() < 25 { continue; }
|
if date_re.is_match(stripped) && stripped.len() < 25 { continue; }
|
||||||
if stripped.starts_with("## ") {
|
if let Some(h) = stripped.strip_prefix("## ") {
|
||||||
return stripped[3..].to_string();
|
return h.to_string();
|
||||||
} else if stripped.starts_with("# ") {
|
} else if let Some(h) = stripped.strip_prefix("# ") {
|
||||||
return stripped[2..].to_string();
|
return h.to_string();
|
||||||
} else {
|
} else {
|
||||||
return if stripped.len() > 70 {
|
return util::truncate(stripped, 67, "...");
|
||||||
let mut end = 67;
|
|
||||||
while !stripped.is_char_boundary(end) { end -= 1; }
|
|
||||||
format!("{}...", &stripped[..end])
|
|
||||||
} else {
|
|
||||||
stripped.to_string()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String::from("(untitled)")
|
String::from("(untitled)")
|
||||||
|
|
|
||||||
|
|
@ -118,9 +118,9 @@ fn format_nodes_section(store: &Store, items: &[ReplayItem], graph: &Graph) -> S
|
||||||
// Content (truncated for large nodes)
|
// Content (truncated for large nodes)
|
||||||
let content = &node.content;
|
let content = &node.content;
|
||||||
if content.len() > 1500 {
|
if content.len() > 1500 {
|
||||||
let end = content.floor_char_boundary(1500);
|
let truncated = crate::util::truncate(content, 1500, "\n[...]");
|
||||||
out.push_str(&format!("\nContent ({} chars, truncated):\n{}\n[...]\n\n",
|
out.push_str(&format!("\nContent ({} chars, truncated):\n{}\n\n",
|
||||||
content.len(), &content[..end]));
|
content.len(), truncated));
|
||||||
} else {
|
} else {
|
||||||
out.push_str(&format!("\nContent:\n{}\n\n", content));
|
out.push_str(&format!("\nContent:\n{}\n\n", content));
|
||||||
}
|
}
|
||||||
|
|
@ -248,12 +248,7 @@ fn format_pairs_section(
|
||||||
// Node A
|
// Node A
|
||||||
out.push_str(&format!("\n### {} ({})\n", a, ca));
|
out.push_str(&format!("\n### {} ({})\n", a, ca));
|
||||||
if let Some(node) = store.nodes.get(a) {
|
if let Some(node) = store.nodes.get(a) {
|
||||||
let content = if node.content.len() > 500 {
|
let content = crate::util::truncate(&node.content, 500, "...");
|
||||||
let end = node.content.floor_char_boundary(500);
|
|
||||||
format!("{}...", &node.content[..end])
|
|
||||||
} else {
|
|
||||||
node.content.clone()
|
|
||||||
};
|
|
||||||
out.push_str(&format!("Weight: {:.2}\n{}\n",
|
out.push_str(&format!("Weight: {:.2}\n{}\n",
|
||||||
node.weight, content));
|
node.weight, content));
|
||||||
}
|
}
|
||||||
|
|
@ -261,12 +256,7 @@ fn format_pairs_section(
|
||||||
// Node B
|
// Node B
|
||||||
out.push_str(&format!("\n### {} ({})\n", b, cb));
|
out.push_str(&format!("\n### {} ({})\n", b, cb));
|
||||||
if let Some(node) = store.nodes.get(b) {
|
if let Some(node) = store.nodes.get(b) {
|
||||||
let content = if node.content.len() > 500 {
|
let content = crate::util::truncate(&node.content, 500, "...");
|
||||||
let end = node.content.floor_char_boundary(500);
|
|
||||||
format!("{}...", &node.content[..end])
|
|
||||||
} else {
|
|
||||||
node.content.clone()
|
|
||||||
};
|
|
||||||
out.push_str(&format!("Weight: {:.2}\n{}\n",
|
out.push_str(&format!("Weight: {:.2}\n{}\n",
|
||||||
node.weight, content));
|
node.weight, content));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,10 +106,11 @@ pub fn differentiate_hub_with_graph(store: &Store, hub_key: &str, graph: &Graph)
|
||||||
|
|
||||||
// Only propose move if there's a reasonable match
|
// Only propose move if there's a reasonable match
|
||||||
if best_sim > 0.05 && !best_section.is_empty() {
|
if best_sim > 0.05 && !best_section.is_empty() {
|
||||||
let snippet = neighbor_content.lines()
|
let snippet = crate::util::first_n_chars(
|
||||||
.find(|l| !l.is_empty() && !l.starts_with("<!--") && !l.starts_with("##"))
|
neighbor_content.lines()
|
||||||
.unwrap_or("")
|
.find(|l| !l.is_empty() && !l.starts_with("<!--") && !l.starts_with("##"))
|
||||||
.chars().take(80).collect::<String>();
|
.unwrap_or(""),
|
||||||
|
80);
|
||||||
|
|
||||||
moves.push(LinkMove {
|
moves.push(LinkMove {
|
||||||
neighbor_key: neighbor_key.to_string(),
|
neighbor_key: neighbor_key.to_string(),
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ pub fn replay_queue_with_graph(
|
||||||
let pos = positions.get(key);
|
let pos = positions.get(key);
|
||||||
let outlier_score = pos.map(|p| p.outlier_score).unwrap_or(0.0);
|
let outlier_score = pos.map(|p| p.outlier_score).unwrap_or(0.0);
|
||||||
let classification = pos
|
let classification = pos
|
||||||
.map(|p| spectral::classify_position(p))
|
.map(spectral::classify_position)
|
||||||
.unwrap_or("unknown");
|
.unwrap_or("unknown");
|
||||||
|
|
||||||
let priority = consolidation_priority(
|
let priority = consolidation_priority(
|
||||||
|
|
|
||||||
|
|
@ -404,13 +404,13 @@ fn execute_parsed(
|
||||||
has_sort = true;
|
has_sort = true;
|
||||||
let asc = *ascending;
|
let asc = *ascending;
|
||||||
results.sort_by(|a, b| {
|
results.sort_by(|a, b| {
|
||||||
let va = a.fields.get(field).and_then(|v| as_num(v));
|
let va = a.fields.get(field).and_then(as_num);
|
||||||
let vb = b.fields.get(field).and_then(|v| as_num(v));
|
let vb = b.fields.get(field).and_then(as_num);
|
||||||
let ord = match (va, vb) {
|
let ord = match (va, vb) {
|
||||||
(Some(a), Some(b)) => a.total_cmp(&b),
|
(Some(a), Some(b)) => a.total_cmp(&b),
|
||||||
_ => {
|
_ => {
|
||||||
let sa = a.fields.get(field).map(|v| as_str(v)).unwrap_or_default();
|
let sa = a.fields.get(field).map(as_str).unwrap_or_default();
|
||||||
let sb = b.fields.get(field).map(|v| as_str(v)).unwrap_or_default();
|
let sb = b.fields.get(field).map(as_str).unwrap_or_default();
|
||||||
sa.cmp(&sb)
|
sa.cmp(&sb)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -89,12 +89,7 @@ pub fn search(query: &str, store: &impl StoreView) -> Vec<SearchResult> {
|
||||||
.take(3)
|
.take(3)
|
||||||
.map(|l| {
|
.map(|l| {
|
||||||
let t = l.trim();
|
let t = l.trim();
|
||||||
if t.len() > 100 {
|
crate::util::truncate(t, 97, "...")
|
||||||
let end = t.floor_char_boundary(97);
|
|
||||||
format!("{}...", &t[..end])
|
|
||||||
} else {
|
|
||||||
t.to_string()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n ");
|
.join("\n ");
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
macro_rules! capnp_enum {
|
macro_rules! capnp_enum {
|
||||||
($rust_type:ident, $capnp_type:path, [$($variant:ident),+ $(,)?]) => {
|
($rust_type:ident, $capnp_type:path, [$($variant:ident),+ $(,)?]) => {
|
||||||
impl $rust_type {
|
impl $rust_type {
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
pub(crate) fn to_capnp(&self) -> $capnp_type {
|
pub(crate) fn to_capnp(&self) -> $capnp_type {
|
||||||
match self {
|
match self {
|
||||||
$(Self::$variant => <$capnp_type>::$variant,)+
|
$(Self::$variant => <$capnp_type>::$variant,)+
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ impl StoreView for MmapView {
|
||||||
fn for_each_node<F: FnMut(&str, &str, f32)>(&self, mut f: F) {
|
fn for_each_node<F: FnMut(&str, &str, f32)>(&self, mut f: F) {
|
||||||
let snap = self.snapshot();
|
let snap = self.snapshot();
|
||||||
for (key, node) in snap.nodes.iter() {
|
for (key, node) in snap.nodes.iter() {
|
||||||
f(&key, &node.content, node.weight);
|
f(key, &node.content, node.weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,19 @@ pub fn memory_subdir(name: &str) -> Result<PathBuf, String> {
|
||||||
.map_err(|e| format!("create {}: {}", dir.display(), e))?;
|
.map_err(|e| format!("create {}: {}", dir.display(), e))?;
|
||||||
Ok(dir)
|
Ok(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Truncate text to `max_len` bytes at a char boundary, appending `suffix`.
|
||||||
|
/// Returns the original string if it's already short enough.
|
||||||
|
pub fn truncate(text: &str, max_len: usize, suffix: &str) -> String {
|
||||||
|
if text.len() <= max_len {
|
||||||
|
text.to_string()
|
||||||
|
} else {
|
||||||
|
let end = text.floor_char_boundary(max_len);
|
||||||
|
format!("{}{}", &text[..end], suffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Take the first `n` chars from a string.
|
||||||
|
pub fn first_n_chars(s: &str, n: usize) -> String {
|
||||||
|
s.chars().take(n).collect()
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue