Weave documentation
Basis

Usage with the SDK

Driving Basis through WeaveNode — the recommended entry point for most applications.

Why use the SDK

Constructing a Basis directly requires you to manage two Strand instances, their storage paths, and any required transport. The weave-sdk crate manages local stores and exposes a small set of namespaced calls on a single WeaveNode.

use weave_sdk::prelude::*;
use uuid::Uuid;

#[tokio::main]
async fn main() -> WeaveResult<()> {
    let node = WeaveNode::builder()
        .namespace("l1fe")
        .identifier("rag-agent")
        .storage_dir("/tmp/weave-rag")
        .build()
        .await?;

    // One Basis per logical corpus. Multiple corpora are fine — they share the node.
    node.open_basis("docs").await?;

    // Insert. The id is whatever you use to look up the source text later.
    let doc_id = Uuid::new_v4();
    let embedding: Vec<f32> = vec![0.01; 768]; // your real model goes here
    node.basis_add("docs", doc_id, &embedding).await?;

    // Search.
    let query: Vec<f32> = vec![0.01; 768];
    let hits: Vec<(Uuid, f32)> = node.basis_search("docs", &query, 10).await?;
    for (id, distance) in hits {
        println!("{id} @ L2 = {distance:.4}");
    }

    Ok(())
}

SDK surface for Basis

CallReturnsNotes
node.open_basis(name)WeaveResult<()>Idempotent; first call creates the strand pair
node.basis_add(name, id, vector)WeaveResult<()>One Strand append + one HNSW insert
node.basis_search(name, query, k)WeaveResult<Vec<(Uuid, f32)>>Returns up to k L2-nearest entries
node.basis_remove(name, id)WeaveResult<()>Present-id removal can deadlock in the current implementation; see recovery limits
node.basis()&Arc<RwLock<BasisStore>>Access the named store manager; this is not a Basis handle

A complete RAG ingestion loop

use weave_sdk::prelude::*;
use uuid::Uuid;

struct Chunk {
    id: Uuid,
    text: String,
    embedding: Vec<f32>,
}

async fn ingest(node: &WeaveNode, chunks: &[Chunk]) -> WeaveResult<()> {
    for c in chunks {
        node.basis_add("docs", c.id, &c.embedding).await?;
        // Persist the source text under a parallel Lens so search results can be
        // resolved back to readable content.
        node.lens_put("docs-text", c.id.as_bytes(), c.text.as_bytes()).await?;
    }
    Ok(())
}

async fn search(node: &WeaveNode, query_emb: &[f32], k: usize) -> WeaveResult<Vec<String>> {
    let hits = node.basis_search("docs", query_emb, k).await?;
    let mut out = Vec::with_capacity(hits.len());
    for (id, _dist) in hits {
        if let Some(bytes) = node.lens_get("docs-text", id.as_bytes()).await? {
            out.push(String::from_utf8_lossy(&bytes).into_owned());
        }
    }
    Ok(out)
}

This pattern — Basis for the index, Lens for the resolved text — is the standard RAG layout in Weave. See the Semantic Search tutorial for the full end-to-end build.

Errors and recovery

SurfaceError you will seeWhat to do
open_basis on already-open nameReturns Ok(()) (idempotent)No action required
basis_add failsAn error from the store or underlying appendInspect the durable log before retrying; do not assume rollback or retry idempotence
Mismatched vector dimensionsComparison can panic or use only part of a vectorValidate equal dimensions and finite values before add/search
Process restartStored vector records may survive, but the new HNSW index is emptyNo public automatic replay is provided; follow the recovery limitations

Recovery and sizing limits

Basis::new creates an empty in-memory index. BasisStore::open does not replay persisted vectors. The private rebuild helper is not a public recovery API, and present-id removal can wait on a lock it already holds. See Snapshot and Recovery.

The checkout has no basis_load example or published memory benchmark supporting a fixed sizing figure. Benchmark representative vectors, dimensions and query loads in your own integration. This guide demonstrates in-process indexing; it does not establish restart-safe search.

On this page