RagMeUp.Client 0.2.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package RagMeUp.Client --version 0.2.0
                    
NuGet\Install-Package RagMeUp.Client -Version 0.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RagMeUp.Client" Version="0.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RagMeUp.Client" Version="0.2.0" />
                    
Directory.Packages.props
<PackageReference Include="RagMeUp.Client" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RagMeUp.Client --version 0.2.0
                    
#r "nuget: RagMeUp.Client, 0.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RagMeUp.Client@0.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RagMeUp.Client&version=0.2.0
                    
Install as a Cake Addin
#tool nuget:?package=RagMeUp.Client&version=0.2.0
                    
Install as a Cake Tool

RAG engine

Document RAG over Postgres + pgvector. The engine owns ingestion, embedding, and reranking; the app owns filtered retrieval as direct SQL on tables it owns.

service/   Python FastAPI engine (`ragmeup` package): /embed, /documents, /rerank
client/    C# read-path library (RagMeUp.Client): rank, build passages, rerank, read spans
db/        schema.sql — documents + chunks tables, HNSW + GIN indexes

Each half carries its own Azure DevOps pipeline (*/azure-pipelines.yml) and releases independently — the client to nuget.org, the engine as a container image. See CI/CD.

Database

psql "$DATABASE_URL" -f db/schema.sql   # needs pgvector >= 0.7.0

Service

Models live on a volume mounted at /models, not baked into the image, so code changes rebuild and push only small layers. A one-shot seed populates the volume.

cd service
DATABASE_URL=postgres://… docker compose up --build

Compose runs model-seed first (downloads weights to the models volume), then starts engine offline against it. On OpenShift, the equivalent is a PVC at /models seeded by an init Job (or initContainer) running python -m ragmeup.download_models.

Local dev without containers: pip install -r requirements.txt then DATABASE_URL=… uvicorn ragmeup.main:app (from service/). Models download from the hub on first run.

Image flavours

One Dockerfile, two bases, selected by FLAVOR — the shared tail (deps, app, entrypoint) is written once so the flavours cannot drift:

docker build .                          # cuda, the default — GPU host (~8 GB)
docker build --build-arg FLAVOR=cpu .   # cpu — CI and a GPU-less VPS (~2.4 GB)

The CPU flavour installs torch from PyTorch's CPU wheel index; a plain pip install torch would pull the CUDA build and ~2.5 GB of nvidia-* wheels that a CPU host can never use.

Configuring a container

Everything is environment variables — the image has no config file. Only DATABASE_URL is required; every other knob has a working default, and the model paths are already set in the image.

docker run -d --name rag-engine \
  -e DATABASE_URL=postgres://user:pass@db:5432/ragdb \
  -e RAG_TORCH_THREADS=4 \
  -v models:/models -e HF_HUB_OFFLINE=1 -e TRANSFORMERS_OFFLINE=1 \
  -p 127.0.0.1:8000:8000 \
  ghcr.io/jgauffin/ragmeup-engine:latest
Variable Default Effect
DATABASE_URL required unless RAG_TARGETS is set Postgres+pgvector for the engine's own (privileged) ingestion pool. Also the default ingestion target — the database /documents writes to when a request names none.
RAG_TARGETS unset JSON {name: dsn}, inline or a path to a file, naming the databases one engine may ingest into. Lets several applications share one loaded set of models — see sharing one engine.
RAG_TARGET_POOL_MIN / RAG_TARGET_POOL_MAX 1 / 4 Connections held per target. The ceiling is unchanged from before; the floor is 1 so an idle target costs one connection, not four.
HF_HUB_OFFLINE, TRANSFORMERS_OFFLINE unset Set to 1 when /models is seeded, so nothing ever reaches the hub at runtime.
BGE_M3_PATH /models/bge-m3 Embedder weights. Set in the image; override only to relocate the volume.
RERANKER_PATH /models/bge-reranker-v2-m3 Default reranker weights. Likewise.
RERANKER_CROSS_ENCODER_PATH cross-encoder/ms-marco-MiniLM-L6-v2 Weights for the cross-encoder backend.
RAG_CHUNK_STRATEGY whitespace windows recursive / structure-aware selects the separator-hierarchy splitter. Changing it only affects newly ingested documents.
RAG_EMBED_DEVICE torch's choice e.g. cuda:0, cpu. Not a model selector: the embedding model is fixed by the schema's column dimensions — swapping it is a re-index event.
RAG_RERANKER_BACKEND multilingual bge cross-encoder selects the ~22M-param English-only model — a different quality/latency point, much cheaper on CPU.
RAG_RERANKER_BY_LANGUAGE unset Per-language override, e.g. en:cross-encoder,sv:flag.
RAG_RERANK_DEVICE torch's choice Device for the reranker specifically.
RAG_RERANK_MAX_LENGTH 512 Tokens read per (query, passage) pair; the tail is silently dropped. Near-linear on latency — the bluntest dial there is.
RAG_RERANK_QUANTIZE off 1/true enables int8 dynamic quantization: ~2.8x faster on CPU, but ~1.1 GB more resident memory. Speed for memory, not free.
RAG_TORCH_THREADS every logical core Cap intra-op threads. Set it to the physical core count on a VPS — see below.
RAG_ENRICHERS none (no-op) contextual or extraction. Both call an LLM per document at ingest, so cost is opt-in.
ENRICH_LLM_BASE_URL OpenAI Any OpenAI-compatible endpoint (vLLM, Ollama, LM Studio).
ENRICH_LLM_API_KEY not-needed Required for real OpenAI; self-hosted endpoints usually ignore it.
ENRICH_LLM_MODEL gpt-4o-mini Model used by the enrichers.
RAG_EXTRACTION_PROMPT built-in template Inline template, or a path to one, for the extraction enricher.
RAG_EXTRACTION_FIELDS unset JSON {name: type} map. Set, only declared and correctly-typed keys survive — which is what keeps the jsonb @> filter reliable.

Two of these are not really optional in production:

  • Publish on loopback, not 0.0.0.0. No endpoint is authenticated and /documents writes with a privileged connection that bypasses your RLS by design. The engine is a trusted internal service — see deployment: trust boundary & scaling.
  • Set RAG_TORCH_THREADS. Unset, torch claims every logical core, which over-subscribes SMT pairs (measured: 8 → 16 threads at 0.98x, i.e. slower), and concurrent /rerank calls then fight for the whole machine.

service/docker-compose.vps.yml is a ready-made version of all of this for a Linux VPS: it pulls the published image, reserves no GPU, binds loopback, and reads the knobs above from a .env file.

Client

The app builds an NpgsqlDataSource with UseVector() and points HttpClient.BaseAddress at the engine. Filters live in the app's own SQL, which produces the candidate chunk-id set passed to Rank.

var q        = await rag.Embed(query);            // engine /embed
long[] ids   = await app.FilteredCandidates(...); // app's WHERE: RLS, geo, joins
var matches  = await rag.Rank(conn, q, query, ids, k: 50); // same `query` as embedded
var passages = rag.BuildPassages(matches);
var top      = await rag.Rerank(query, passages, topK: 5);
var spans    = await rag.ReadSpans(conn, top);    // clean original text for /answer

Methods drop the Async suffix — the whole client is async, so it adds nothing.

Tests

Both sides isolate the expensive boundaries (the GPU models, the database) so the logic around them is tested directly, and a thin layer of integration tests exercises the real SQL against Postgres+pgvector.

A throwaway pgvector instance for the integration tests:

docker run -d --name rag-test-pg -e POSTGRES_PASSWORD=test -e POSTGRES_DB=ragtest \
  -p 55432:5432 pgvector/pgvector:pg16

Service (service/) — unit tests need no model and no DB (the model boundary is mocked); the /documents integration test needs the DB above.

cd service
python -m venv .venv && .venv/Scripts/pip install -r requirements-dev.txt
.venv/Scripts/python -m pytest                       # unit only (integration auto-skips)
RAG_TEST_DATABASE_URL=postgres://postgres:test@localhost:55432/ragtest \
  .venv/Scripts/python -m pytest                     # + the /documents integration test

Client (client/) — unit tests are pure (no DB/HTTP). The integration tests use RAG_TEST_DATABASE_URL if set, otherwise spin a pgvector container via Testcontainers; they auto-skip when neither is available.

cd client
dotnet test tests/RagMeUp.Client.Tests               # unit
RAG_TEST_DATABASE_URL=postgres://… \
  dotnet test tests/RagMeUp.Client.IntegrationTests  # rank/read-path vs real pgvector

Both suites honour the same RAG_TEST_DATABASE_URL, so one local pgvector serves both. The integration tests reset their tables on start, so point it at a scratch database, not one with data you care about.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.5.0 156 8/18/2026 0.5.0 is deprecated because it is no longer maintained.
0.4.0 159 8/7/2026
0.3.0 113 8/7/2026
0.2.0 139 8/4/2026
0.1.0 130 8/2/2026