amoxtli

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 12, 2026 License: MIT Imports: 15 Imported by: 0

README

Amoxtli

Amoxtli — « livre, codex » en nahuatl.

Bibliothèque Go d'indexation documentaire multi-backend et d'ingestion de fichiers : recherche plein-texte (bleve), recherche vectorielle (sqlite-vec), recherche hybride PostgreSQL (pgvector + FTS natif), fusion pondérée des résultats, découpage markdown en sections, conversion de fichiers (pandoc, LibreOffice, OCR/LLM) et sauvegarde/restauration des index.

Extraite du projet bornholm/corpus, dont elle constitue le cœur, mais indépendante de celui-ci.

Statut : pré-v0.1.0 — API instable.

Installation

go get github.com/bornholm/amoxtli

⚠️ Directive replace obligatoire : le backend index/sqlitevec dépend d'un fork des bindings sqlite-vec. Les directives replace ne se propageant pas aux consommateurs, ajoutez à votre go.mod :

replace github.com/asg017/sqlite-vec-go-bindings => github.com/Bornholm/sqlite-vec-go-bindings v0.0.0-20250407170538-55971919e573

Démarrage rapide

Le magasin de documents (WithStore) et les indexeurs (WithIndexers) sont fournis explicitement, chacun construit par son propre constructeur. L'appelant possède les ressources qu'il crée et doit les fermer ; codex.Close() n'arrête que le runner de tâches.

// Magasin de documents (SQLite local, ou gorm.NewPostgresStore).
store, err := gorm.NewSQLiteStore("/data/kb/data.sqlite") // ingest/gorm
if err != nil { /* ... */ }
defer store.Close()

// Index plein-texte (bleve).
bleveIdx, err := bleve.OpenOrCreate(ctx, "/data/kb/index.bleve") // index/bleve
if err != nil { /* ... */ }
defer bleveIdx.Close()

codex, err := amoxtli.New(ctx,
    amoxtli.WithStore(store),
    amoxtli.WithIndexers(amoxtli.Indexer{ID: "bleve", Index: bleveIdx, Weight: 1.0}),
    amoxtli.WithDisableHyDE(), amoxtli.WithDisableJudge(), // pas de client LLM
)
if err != nil { /* ... */ }
defer codex.Close()

collID, _ := codex.CreateCollection(ctx, "docs")
taskID, _ := codex.IndexFile(ctx, collID, "guide.md", file)
results, _ := codex.Search(ctx, "comment faire…", amoxtli.WithSearchMaxResults(5))

Exemples complets et exécutables : example/sqlite (SQLite + bleve, sans LLM) et example/postgres (tout PostgreSQL).

Architecture

Package Rôle
amoxtli (racine) Façade Codex : composition explicite (store + indexeurs), ingestion, recherche, backup
model Modèle de domaine : Document, Section, Collection
index Contrat index.Index + options de recherche
index/bleve Backend plein-texte (bleve)
index/sqlitevec Backend vectoriel (sqlite-vec + embeddings)
index/postgres Backend hybride PostgreSQL (FTS tsvector + pgvector, fusion RRF)
index/pipeline Index composite pondéré + transformers (HyDE, Judge, dédup)
index/testsuite Suite de conformité pour les implémentations de index.Index
markdown Parsing/chunking markdown en sections
convert Conversion de fichiers vers markdown (pandoc, libreoffice, genai)
task / task/memory Exécution de tâches asynchrones
ingest / ingest/gorm Pipeline d'ingestion + magasin de documents (SQLite ou PostgreSQL)
backup Abstraction snapshot/restore
Indexeurs personnalisés

Tout type implémentant index.Index peut être branché dans le pipeline, avec son poids relatif dans la fusion des scores :

codex, err := amoxtli.New(ctx,
    amoxtli.WithStore(store),
    amoxtli.WithIndexers(
        amoxtli.Indexer{ID: "bleve", Index: bleveIdx, Weight: 0.4},
        amoxtli.Indexer{ID: "custom", Index: myIndex, Weight: 0.6},
    ),
)

La conformité se vérifie avec index/testsuite.TestIndex.

Backend PostgreSQL

index/postgres combine le FTS natif (tsvector + unaccent, configuration de langue détectée automatiquement) et pgvector (KNN cosinus, index HNSW), fusionnés par Reciprocal Rank Fusion. Sans client LLM, il fonctionne en plein-texte seul. La base doit disposer des extensions vector et unaccent (images Docker pgvector/pgvector).

Le magasin de documents et l'index peuvent tous deux vivre dans la même base, pour un déploiement entièrement PostgreSQL sans aucun stockage local :

dsn := "postgres://user:pass@localhost:5432/kb?sslmode=disable"

store, err := gorm.NewPostgresStore(ctx, dsn) // ingest/gorm
defer store.Close()

pool, err := pgxpool.New(ctx, dsn) // possédé par l'appelant
defer pool.Close()
pg := postgres.NewIndex(pool, llmClient) // client LLM nil = plein-texte seul

codex, err := amoxtli.New(ctx,
    amoxtli.WithStore(store),
    amoxtli.WithIndexers(amoxtli.Indexer{ID: "postgres", Index: pg, Weight: 1.0}),
)

Voir example/postgres pour un exemple complet et exécutable.

Convertisseurs de fichiers

Binaires externes requis selon le convertisseur : pandoc (convert/pandoc), libreoffice (convert/libreoffice). convert/genai utilise une API d'extraction LLM/OCR (Mistral OCR, Marker).

Tests

go test -short ./...                                             # sans Docker
AMOXTLI_TEST_OLLAMA=1 go test ./index/sqlitevec/ -timeout 20m    # Docker + Ollama
AMOXTLI_TEST_POSTGRES=1 go test ./index/postgres/ -timeout 10m   # Docker + PostgreSQL (FTS seul)
AMOXTLI_TEST_POSTGRES=1 go test ./ingest/gorm/ -timeout 10m      # Docker + PostgreSQL (magasin de documents)
AMOXTLI_TEST_POSTGRES=1 AMOXTLI_TEST_OLLAMA=1 \
  go test ./index/postgres/ -timeout 20m                         # Docker + PostgreSQL + Ollama (hybride)

Licence

MIT

Documentation

Overview

Package amoxtli provides a multi-backend document indexing and file-ingestion library: full-text search (bleve), vector search (sqlite-vec), weighted result merging, markdown chunking, file conversion and snapshot/restore of indexes.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNotFound = ingest.ErrNotFound
	ErrCanceled = task.ErrCanceled
)

Functions

This section is empty.

Types

type Codex

type Codex struct {
	// contains filtered or unexported fields
}

Codex is the main embedded instance: a store, index pipeline and task runner behind a single API.

func New

func New(ctx context.Context, funcs ...Option) (*Codex, error)

New creates a new embedded Codex instance.

A store (WithStore) and an index (WithIndex or WithIndexers) are required; build them with the dedicated constructors, e.g. gorm.NewSQLiteStore / gorm.NewPostgresStore for the store and bleve.OpenOrCreate / sqlitevec.NewIndex / postgres.NewIndex for the indexers. The caller owns the resources it constructs and must Close them; Codex.Close only stops the task runner.

func (*Codex) Backup

func (c *Codex) Backup(ctx context.Context) (io.ReadCloser, error)

Backup streams a snapshot of the index and the document store as a multipart archive.

func (*Codex) CleanupIndex

func (c *Codex) CleanupIndex(ctx context.Context, collections ...model.CollectionID) (task.ID, error)

CleanupIndex schedules a cleanup of orphaned documents and obsolete index entries.

func (*Codex) Close

func (c *Codex) Close() error

Close stops the task runner. Resources passed in through WithStore, WithIndex/WithIndexers and WithTaskRunner are owned by the caller and must be closed by them.

func (*Codex) CreateCollection

func (c *Codex) CreateCollection(ctx context.Context, label string) (model.CollectionID, error)

CreateCollection creates a new collection and returns its ID.

func (*Codex) DeleteBySource

func (c *Codex) DeleteBySource(ctx context.Context, source *url.URL) error

DeleteBySource removes all documents and index entries for the given source URL.

func (*Codex) GetSectionsByIDs

func (c *Codex) GetSectionsByIDs(ctx context.Context, ids []model.SectionID) (map[model.SectionID]model.Section, error)

GetSectionsByIDs returns the sections matching the given IDs, typically used to retrieve the content behind search results.

func (*Codex) Index

func (c *Codex) Index() index.Index

Index returns the underlying index.Index for advanced usage.

func (*Codex) IndexFile

func (c *Codex) IndexFile(ctx context.Context, collectionID model.CollectionID, filename string, r io.Reader, funcs ...IndexFileOption) (task.ID, error)

IndexFile indexes a file into the given collection. Returns a task.ID that can be used to track progress via TaskState.

func (*Codex) Manager

func (c *Codex) Manager() *ingest.Manager

Manager returns the underlying ingest.Manager for advanced usage.

func (*Codex) Reindex

func (c *Codex) Reindex(ctx context.Context) (task.ID, error)

Reindex schedules a rebuild of the whole index.

func (*Codex) ReindexCollection

func (c *Codex) ReindexCollection(ctx context.Context, collectionID model.CollectionID) (task.ID, error)

ReindexCollection schedules a rebuild of the index for a single collection.

func (*Codex) Restore

func (c *Codex) Restore(ctx context.Context, r io.Reader) error

Restore synchronously restores a snapshot previously produced by Backup.

func (*Codex) Search

func (c *Codex) Search(ctx context.Context, query string, funcs ...SearchOption) ([]*index.SearchResult, error)

Search performs a semantic search across the indexed documents.

func (*Codex) TaskState

func (c *Codex) TaskState(ctx context.Context, id task.ID) (*task.State, error)

TaskState returns the current state of an indexing task.

type IndexFileOption

type IndexFileOption func(*IndexFileOptions)

IndexFileOption configures an IndexFile call.

func WithIndexFileCollections

func WithIndexFileCollections(ids ...model.CollectionID) IndexFileOption

WithIndexFileCollections associates the indexed file with the given collection IDs.

func WithIndexFileETag

func WithIndexFileETag(etag string) IndexFileOption

WithIndexFileETag sets the ETag for the indexed file (used for deduplication).

func WithIndexFileSource

func WithIndexFileSource(source *url.URL) IndexFileOption

WithIndexFileSource sets the source URL for the indexed file.

type IndexFileOptions

type IndexFileOptions struct {
	Source      *url.URL
	ETag        string
	Collections []model.CollectionID
}

IndexFileOptions holds options for IndexFile calls.

type Indexer

type Indexer struct {
	// ID identifies the indexer in the pipeline (e.g. "bleve", "postgres").
	ID string
	// Index is any implementation of the index.Index contract.
	Index index.Index
	// Weight is the relative weight of this indexer when merging scores.
	Weight float64
}

Indexer identifies a weighted index.Index inside the search pipeline.

type Option

type Option func(*options)

Option is a function that configures a Codex instance.

func WithDisableHyDE

func WithDisableHyDE() Option

WithDisableHyDE disables the HyDE query transformer.

func WithDisableJudge

func WithDisableJudge() Option

WithDisableJudge disables the Judge results transformer.

func WithFileConverter

func WithFileConverter(fc convert.Converter) Option

WithFileConverter sets a file converter for converting files before indexing.

func WithIndex

func WithIndex(idx index.Index) Option

WithIndex provides a ready-made index.Index, bypassing pipeline composition entirely (including the HyDE/Judge/dedup transformers). Mutually exclusive with WithIndexers. The caller owns and must close the index.

func WithIndexers

func WithIndexers(indexers ...Indexer) Option

WithIndexers declares the set of indexers composing the search pipeline, each with its relative weight. It can be called several times; indexers accumulate.

Any implementation of index.Index can be plugged in; conformance can be verified with the index/testsuite package. Build the backends with their own constructors, e.g. bleve.OpenOrCreate(...), sqlitevec.NewIndex(...) or postgres.NewIndex(...), and wrap each in an Indexer.

func WithLLMClient

func WithLLMClient(client llm.Client) Option

WithLLMClient sets the LLM client used by the HyDE and Judge transformers.

func WithMaxTotalWords

func WithMaxTotalWords(n int) Option

WithMaxTotalWords sets the maximum total words used by the Judge transformer.

func WithMaxWordsPerSection

func WithMaxWordsPerSection(n int) Option

WithMaxWordsPerSection sets the maximum number of words per document section.

func WithSnapshotBoundary

func WithSnapshotBoundary(boundary string) Option

WithSnapshotBoundary overrides the multipart boundary used by Backup/Restore.

func WithStore

func WithStore(store ingest.Store) Option

WithStore sets the document store. It is required. Build it with gorm.NewSQLiteStore or gorm.NewPostgresStore (or any ingest.Store). The caller owns and must close the store.

func WithTaskParallelism

func WithTaskParallelism(n int) Option

WithTaskParallelism sets the number of concurrent tasks allowed.

func WithTaskRunner

func WithTaskRunner(runner task.Runner) Option

WithTaskRunner provides a custom task.Runner implementation.

type SearchOption

type SearchOption func(*SearchOptions)

SearchOption configures a Search call.

func WithSearchCollections

func WithSearchCollections(ids ...model.CollectionID) SearchOption

WithSearchCollections restricts the search to the given collection IDs.

func WithSearchMaxResults

func WithSearchMaxResults(n int) SearchOption

WithSearchMaxResults sets the maximum number of search results.

type SearchOptions

type SearchOptions struct {
	MaxResults  int
	Collections []model.CollectionID
}

SearchOptions holds options for Search calls.

Directories

Path Synopsis
example
postgres command
Command postgres demonstrates amoxtli backed entirely by PostgreSQL: the document store and the hybrid index (index/postgres) share a single database.
Command postgres demonstrates amoxtli backed entirely by PostgreSQL: the document store and the hybrid index (index/postgres) share a single database.
sqlite command
Command sqlite demonstrates amoxtli backed entirely by local files: a SQLite document store and a Bleve full-text index.
Command sqlite demonstrates amoxtli backed entirely by local files: a SQLite document store and a Bleve full-text index.
postgres
Package postgres provides a hybrid index backed by PostgreSQL, combining native full-text search (tsvector + ts_rank) with vector similarity search (pgvector, cosine distance).
Package postgres provides a hybrid index backed by PostgreSQL, combining native full-text search (tsvector + ts_rank) with vector similarity search (pgvector, cosine distance).
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL