Weave

Introduction

Composable RAG pipeline engine for Go.

Weave is a Go library that handles the full document lifecycle for Retrieval-Augmented Generation: ingest documents, chunk text, generate embeddings, store vectors, retrieve relevant context, and assemble prompts.

Weave is a library — not a service. You bring your own database, vector store, embedder, and HTTP server. Weave provides the RAG plumbing.

What it does

  • Document ingestion — Load, chunk, embed, and store documents in a single engine.Ingest call. Supports single and batch ingestion with configurable concurrency.
  • Semantic retrieval — Retrieve the most relevant chunks via cosine similarity, MMR (Maximal Marginal Relevance), or hybrid search across one or multiple collections.
  • Collection management — Organize documents into named collections with per-collection embedding models, chunking strategies, and custom metadata.
  • Pluggable components — Every subsystem (Loader, Chunker, Embedder, Retriever, VectorStore, MetadataStore) is a Go interface. Swap any component with one line.
  • Multi-tenant isolation — Every collection, document, and chunk is scoped to a tenant via context. Cross-tenant queries are structurally impossible.
  • Extension system — Hook into 14 lifecycle events (ingestion, retrieval, collection, reindex) for metrics, audit trails, or custom processing.
  • Context assembly — Build LLM prompts from retrieved chunks with token budgeting and citation tracking.
  • Pluggable stores — In-memory (testing), PostgreSQL (bun ORM), SQLite, and pgvector. All implement the same composite store interfaces.
  • Forge integration — Drop-in forge.Extension with DI-injected Engine and auto-registered HTTP routes.
  • HTTP API — 12 endpoints for collections, documents, retrieval, and hybrid search.

Design philosophy

Library, not service. Weave is a set of Go packages you import. You control main, the database connection, and the process lifecycle.

Interfaces over implementations. Every subsystem defines a Go interface. Swap any storage or embedding backend with a single type change.

Composable stores. Each subsystem (metadata, vector) has its own store interface. Pass the same backend value wherever a store is needed.

Tenant-scoped by design. weave.WithTenant and weave.WithApp inject context that is enforced at every layer — no cross-tenant data is accessible regardless of what the caller passes.

TypeID everywhere. All entities use type-prefixed, K-sortable, UUIDv7-based identifiers. Passing a doc_ ID where a col_ ID is expected is a compile error.

Quick look

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/xraph/weave"
    memstore "github.com/xraph/weave/store/memory"
    memvec "github.com/xraph/weave/vectorstore/memory"
)

func main() {
    ctx := context.Background()
    ctx = weave.WithTenant(ctx, "tenant-1")
    ctx = weave.WithApp(ctx, "myapp")

    engine, err := weave.NewEngine(
        weave.WithStore(memstore.New()),
        weave.WithVectorStore(memvec.New()),
        weave.WithEmbedder(myEmbedder),
    )
    if err != nil {
        log.Fatal(err)
    }

    // Create a collection.
    col, _ := engine.CreateCollection(ctx, weave.CreateCollectionInput{
        Name:         "product-docs",
        ChunkSize:    512,
        ChunkOverlap: 50,
    })

    // Ingest a document.
    doc, _ := engine.Ingest(ctx, col.ID, weave.IngestInput{
        Title:   "Return Policy",
        Content: "Our return policy allows returns within 30 days...",
        Source:  "faq.md",
    })
    fmt.Printf("ingested doc=%s state=%s chunks=%d\n",
        doc.ID, doc.State, doc.ChunkCount)

    // Retrieve relevant chunks.
    results, _ := engine.Retrieve(ctx, col.ID, &weave.RetrieveInput{
        Query: "How long do I have to return an item?",
        TopK:  3,
    })
    for _, r := range results {
        fmt.Printf("[%.2f] %s\n", r.Score, r.Content[:60])
    }
}

Where to go next

On this page