Documents, splitting, vector stores and retrieval for MoonBit. Embeddings come through a contract, so no model is needed.
async fn answer(embedder : &@moonrag.Embedder) -> Array[@moonrag.Scored] raise {
let store = @moonrag.InMemoryVectorStore::new(embedder)
let _ = store.add([
@moonrag.Document::new(
"the harbour at dawn, fishing boats leaving",
meta=Map([("topic", @json.Json::string("sea"))]),
),
@moonrag.Document::new("a mountain path in autumn, cold and clear"),
])
let query = embedder.embed(["boats in the harbour"])[0]
store.search(
query[:],
limit=1,
filter=@moonrag.Filter::Equal("topic", @json.Json::string("sea")),
)
}moon add moonbitstack/moonragimport {
"moonbitstack/moonrag", // documents, contracts, memory store, retrieval
"moonbitstack/moonrag/split", // recursive, token and HTML splitters
"moonbitstack/moonrag/load", // text, CSV, JSON, Markdown, HTML loaders
"moonbitstack/moonrag/load/file", // the same loaders, reading from disk
"moonbitstack/moonrag/index", // incremental indexing
}let document = @moonrag.Document::new("some text", id="guide.md#2")
.set_meta("source", @json.Json::string("guide.md"))
.set_meta("page", @json.Json::number(3.0))let recent = @moonrag.Filter::All([
@moonrag.Filter::Equal("topic", @json.Json::string("sea")),
@moonrag.Filter::Compare("year", @moonrag.Order::AtLeast, 2025.0),
])pub(open) trait Embedder {
async fn embed(Self, texts : ArrayView[String]) -> Array[Array[Double]] raise
}let store = @moonrag.InMemoryVectorStore::new(embedder)
let ids = store.add([document])
let hits = store.search(query_vector, limit=5)
store.delete([ids[0]][:])let plain : &@moonrag.Retriever = @moonrag.SimilarityRetriever::new(embedder, store)
let diverse : &@moonrag.Retriever = @moonrag.MmrRetriever::new(embedder, store, lambda=0.3)let splitter = @split.RecursiveSplitter::new(chunk_size=1000, chunk_overlap=200)
let chunks = splitter.split(document)let splitter = @split.TokenSplitter::new(count_tokens, chunk_size=512, chunk_overlap=64)let splitter = @split.HtmlSplitter::new(chunk_size=1000)
let sections = splitter.split(@moonrag.Document::new(page_html))
// sections[1].str_meta("h1") == Some("Install"); .str_meta("h2") == Some("Search")| Format | What a document is | Metadata |
|---|---|---|
| TextLoader | the whole text | source |
| CsvLoader | one row, written as column: value lines | source, row, every column |
| JsonLoader | one element of an array, or the object | source, every field except the text key |
| MarkdownLoader | the whole file | source, title from the first heading |
| HtmlLoader | the readable text of the page | source, title |
let documents = @moonrag.Loader::load(@load.CsvLoader::new("people.csv", csv_text))let documents = @file.csv("people.csv")
let text = @file.markdown("notes.md")let indexer = @index.Indexer::new(store)
let stats = indexer.index(chunks) // added / updated / skipped / deleted
let cleaned = indexer.index(chunks, cleanup=@index.Cleanup::Full)| Document | id : String?, text : String, meta : Map[String, Json] |
| Scored | document : Document, score : Double |
| Filter | Equal, OneOf, Exists, Compare, All, Any, Not |
| Embedder | async embed(texts : ArrayView[String]) -> Array[Array[Double]] raise |
| VectorStore | async add(Array[Document]) -> Array[String] raise, async delete(ids) raise, async search(vector, limit~, filter?) -> Array[Scored] raise |
| Retriever | async retrieve(query : String, limit~ : Int32) -> Array[Scored] raise |
| Splitter | split(Document) -> Array[Document] |
| Loader | async load() -> Array[Document] raise |
| Refused | Mismatch, Empty, Exceeded, Malformed, Unsupported |
| # | Example | What it shows |
|---|---|---|
| 01 | ingest | load, split, index, retrieve, then index again and embed nothing |
| 02 | store | add, filter, rank, rerank with MMR, delete |
| 03 | files | the file front end over a real temporary directory |
moon run src/examples/01-ingest --target nativeimpl Embedder for HashingEmbedderasync fn HashingEmbedder::embed(self : HashingEmbedder, texts : ArrayView[String]) -> Array[Array[Double]]impl VectorStore for InMemoryVectorStoreasync fn InMemoryVectorStore::add(self : InMemoryVectorStore, documents : Array[Document]) -> Array[String]fn InMemoryVectorStore::add_vectors(self : InMemoryVectorStore, documents : Array[Document], vectors : ArrayView[Array[Double]]) -> Array[String] raiseasync fn InMemoryVectorStore::search(self : InMemoryVectorStore, vector : ArrayView[Double], limit~ : Int, filter? : Filter) -> Array[Scored]pub struct MmrRetriever {
embedder : &Embedder
store : &VectorStore
filter : Filter?
lambda : Double
fetch_limit : Int
}impl Retriever for MmrRetrieverfn MmrRetriever::new(embedder : &Embedder, store : &VectorStore, lambda? : Double, fetch_limit? : Int, filter? : Filter) -> MmrRetrieverimpl Retriever for SimilarityRetrieverfn SimilarityRetriever::new(embedder : &Embedder, store : &VectorStore, filter? : Filter) -> SimilarityRetrieverasync fn SimilarityRetriever::retrieve(self : SimilarityRetriever, query : String, limit~ : Int) -> Array[Scored]Install
Download zipDocuments, splitting, vector stores and retrieval for MoonBit. Embeddings come through a contract, so no model is needed.