Research Papers & Code Repos
Reach for ResearchPaperCorpus when you want a paper corpus with an
opt-in citation graph; reach for CodeRepoMemory when you want
“function body as-of commit N” — point-in-time recall over committed code.
Both wrap DocumentCorpus
on the ingest side; CodeRepoMemory adds TemporalQuery<Documents>
on the recall side.
ResearchPaperCorpus
crates/lunaris-recipes/src/documentary/research_paper_corpus.rs — a
DocumentCorpus plus an opt-in citation graph. The graph-on path toggles
Lunaris::graph_pipeline().enable() (the graph defaults OFF per blueprint
§5.2; opt in via the explicit builder call).
| Method | Signature | Notes |
|---|---|---|
new | fn new(lunaris: Arc<Lunaris>, scope: Scope, source_prefix: impl Into<String>) -> Self | binds the inner corpus (e.g. "papers:") |
with_graph_pipeline | fn with_graph_pipeline(self, on: bool) -> Self | enable() / disable() on the graph handle; idempotent; builder-style; consumes self |
ingest | async fn ingest(chunks: Vec<(String, serde_json::Map<String, serde_json::Value>)>) -> Result<(), LunarisError> | forwards to DocumentCorpus::ingest |
search | async fn search(self, query: &str) -> Result<Vec<Hit>, LunarisError> | forwards to DocumentCorpus::search; consumes self |
Put the paper’s id / venue / year in the chunk metadata so a later
metadata-Eq filter (via the underlying DocumentCorpus) can narrow by
year or venue. To get citation edges in the graph, call
with_graph_pipeline(true) before ingest — extraction runs inside the
ingest hot path.
Example
Shaped after research_paper_corpus_parity_graph_off_recall in
crates/lunaris-recipes/tests/documentary_parity.rs:
use std::sync::Arc;
use lunaris::{Lunaris, Scope};
use lunaris_recipes::documentary::ResearchPaperCorpus;
#[tokio::main]
async fn main() -> Result<(), lunaris::LunarisError> {
let lunaris = Arc::new(Lunaris::open("moon://localhost:6380").await?);
// The partition every recipe below reads and writes in.
let scope = Scope::new("acme-research")?;
// Opt in to the citation graph before ingest. Default is OFF.
let papers = ResearchPaperCorpus::new(lunaris.clone(), scope.clone(), "papers:")
.with_graph_pipeline(true);
let chunks = vec![
(
"Reciprocal Rank Fusion outperforms Condorcet fusion on TREC runs.".to_string(),
serde_json::Map::from_iter([
("paper_id".to_string(), serde_json::json!("cormack2009")),
("year".to_string(), serde_json::json!(2009)),
]),
),
(
"ACT-R base-level activation models declarative memory decay.".to_string(),
serde_json::Map::from_iter([
("paper_id".to_string(), serde_json::json!("anderson1996")),
("year".to_string(), serde_json::json!(1996)),
]),
),
];
papers.ingest(chunks).await?;
let hits = papers.search("how is rank fusion evaluated?").await?;
for h in &hits {
println!("score={:.3} source={} text={}", h.score, h.source, h.text);
}
Ok(())
}
CodeRepoMemory
crates/lunaris-recipes/src/documentary/code_repo_memory.rs — models
“function body as-of commit N”. Each commit is ingested once per chunk with
commit_sha and committer_date_unix_ms stamped into the Episode
metadata; recall time-travels via TemporalQuery::<Documents>::as_of.
| Method | Signature | Notes |
|---|---|---|
new | fn new(lunaris: Arc<Lunaris>, scope: Scope, repo_prefix: impl Into<String>) -> Self | binds the inner corpus (e.g. "repo:lunaris/") |
ingest_commit | async fn ingest_commit(commit_sha: impl Into<String>, committer_date_unix_ms: i64, chunks: Vec<(String, serde_json::Map<String, serde_json::Value>)>) -> Result<(), LunarisError> | stamps commit_sha + committer_date_unix_ms into each chunk’s metadata, then forwards to DocumentCorpus::ingest (1 primitive call) |
recall | async fn recall(query: &str, as_of: Hlc) -> Result<Vec<Hit>, LunarisError> | 2 primitive calls: TemporalQuery::<Documents>::new + .as_of(ts).execute(query) |
Hlc’s native shape is Unix-milliseconds ({wall_ms: u64, counter: u32, node_id: u16}) — there is nowhere on the Hlc surface for RFC3339-nanos.
ingest_commit takes the committer date as an i64 of Unix-ms;
recall(..., as_of: Hlc) takes the Hlc directly, so you control the
counter / node-id disambiguation in dense-commit scenarios. Build one with
Hlc::from_parts(unix_ms as u64, 0, 0).
TemporalQueryrecalls across all Documents —CodeRepoMemorydoes not partition by repo at the recall layer. If you store more than one repo on the same handle, isolate them with distinct prefixes and a metadata filter, or use separate handles. The parity tests are fixture-isolated.
Example
Shaped after code_repo_memory_as_of_commit_50_round_trip_moon_postgres in
crates/lunaris-recipes/tests/documentary_rust_integration.rs:
use std::sync::Arc;
use lunaris::{Lunaris, Scope};
use lunaris_core::hlc::Hlc;
use lunaris_recipes::documentary::CodeRepoMemory;
#[tokio::main]
async fn main() -> Result<(), lunaris::LunarisError> {
let lunaris = Arc::new(Lunaris::open("moon://127.0.0.1:6380").await?);
// The partition every recipe below reads and writes in.
let scope = Scope::new("acme-research")?;
let repo = CodeRepoMemory::new(lunaris.clone(), scope.clone(), "repo:lunaris/");
// Commit A — first version of the function.
let commit_a_ms: i64 = 1_715_000_000_000; // committer date, Unix-ms
repo.ingest_commit(
"a1b2c3",
commit_a_ms,
vec![(
"fn recall(query: &str) -> Vec<Hit> { /* v1 */ }".to_string(),
serde_json::Map::from_iter([("path".to_string(), serde_json::json!("src/recall.rs"))]),
)],
).await?;
// Commit B — the function changed.
let commit_b_ms: i64 = 1_715_600_000_000;
repo.ingest_commit(
"d4e5f6",
commit_b_ms,
vec![(
"fn recall(query: &str, as_of: Hlc) -> Vec<Hit> { /* v2 */ }".to_string(),
serde_json::Map::from_iter([("path".to_string(), serde_json::json!("src/recall.rs"))]),
)],
).await?;
// Time-travel: what did `recall` look like at commit A's timestamp?
let as_of_a = Hlc::from_parts(commit_a_ms as u64, 0, 0);
let hits = repo.recall("recall function signature", as_of_a).await?;
for h in &hits {
println!("source={} text={}", h.source, h.text);
}
Ok(())
}
Notes
recallhas no builder —as_ofis a positionalHlcargument. UseHlc::from_parts(unix_ms as u64, 0, 0)(or capture a causal timestamp fromlunaris.clock().tick()).ingest_commitis oneatomic_writeper chunk, batched per invocation — preserves the INGEST-04 contract at the per-chunk grain.- Graph posture — only
ResearchPaperCorpusexposes a graph toggle;CodeRepoMemorydoes not. For commit-graph-style traversal, drop to theLunarishandle and composeGraph::anchored(...)yourself — see The Graph Pipeline. - For a corpus without either citation graph or time-travel, use
DocumentKnowledgeBase.