windlass.rag.pipeline¶
pipeline
¶
The RAG pipeline.
A :class:RAGPipeline wires the eight components a retrieval-augmented system
needs — loader, preprocessor, chunker, embedder, vector store, retriever,
guardrail, model — and exposes two operations:
- :meth:
RAGPipeline.aingest— load, clean, chunk, embed, index. - :meth:
RAGPipeline.aask— guard, retrieve, prompt, generate, guard.
You rarely construct one directly; :class:~windlass.rag.builder.RAGBuilder
assembles it and injects the cross-component dependencies (the semantic chunker
needs the embedder, the vector retriever needs both the embedder and the store)
so you never wire them by hand.
Every stage is traced, so .observe('console') shows you exactly where the
time went.
Example
from windlass import Windlass rag = Windlass.rag() rag.ingest_text("Windlass is a modular AI application framework.") 1 answer = rag.ask("What is Windlass?") len(answer.contexts) >= 1 True
RAGPipeline
¶
RAGPipeline(
*,
llm: LLM,
embedder: Embedder,
vectorstore: VectorStore,
retriever: Retriever | None = None,
chunker: Chunker | None = None,
loader: Loader | None = None,
preprocessor: Preprocessor | None = None,
guardrail: Guardrail | None = None,
memory: Memory | None = None,
tracer: Tracer | None = None,
prompt: str = DEFAULT_PROMPT,
top_k: int = 5,
max_context_tokens: int = 6000,
include_sources: bool = True,
answer_without_context: bool = False,
batch_size: int = 64
)
A fully wired retrieval-augmented generation pipeline.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
llm
|
LLM
|
Model used to generate answers. |
required |
embedder
|
Embedder
|
Model used to embed chunks and queries. |
required |
vectorstore
|
VectorStore
|
Where chunk vectors are stored. |
required |
retriever
|
Retriever | None
|
Retrieval strategy. Defaults to dense search over
|
None
|
chunker
|
Chunker | None
|
Chunking strategy. Defaults to recursive. |
None
|
loader
|
Loader | None
|
Loader used by :meth: |
None
|
preprocessor
|
Preprocessor | None
|
Optional cleaning/enrichment stage. |
None
|
guardrail
|
Guardrail | None
|
Optional input/output policy. |
None
|
memory
|
Memory | None
|
Optional conversation memory, making |
None
|
tracer
|
Tracer | None
|
Observability backend. |
None
|
prompt
|
str
|
Prompt template. Must contain |
DEFAULT_PROMPT
|
top_k
|
int
|
How many chunks to retrieve per question. |
5
|
max_context_tokens
|
int
|
Ceiling on context placed in the prompt. Chunks are dropped from the tail until it fits, so the highest-ranked context always survives. |
6000
|
include_sources
|
bool
|
Number each context block and ask the model to cite them. |
True
|
answer_without_context
|
bool
|
When False, retrieval finding nothing returns
:data: |
False
|
batch_size
|
int
|
Chunks embedded and indexed per batch during ingestion. |
64
|
Attributes:
| Name | Type | Description |
|---|---|---|
llm |
The generation model. |
|
retriever |
The retrieval strategy. |
|
vectorstore |
The vector store. |
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
When a required component is missing or the prompt template lacks its placeholders. |
Source code in src\windlass\rag\pipeline.py
aingest
async
¶
aingest(
source: Any, *, metadata: dict[str, Any] | None = None, show_progress: bool = False
) -> int
Load, preprocess, chunk, embed and index a source.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Any
|
A path, directory, URL, glob, byte payload, or a sequence of any of those. Directories are walked and each file routed to the right loader by extension. |
required |
metadata
|
dict[str, Any] | None
|
Extra metadata attached to every resulting chunk — a tenant id, a corpus name, an ingestion date. |
None
|
show_progress
|
bool
|
Log a line per stage. Useful on long runs. |
False
|
Returns:
| Type | Description |
|---|---|
int
|
How many chunks were indexed. |
Raises:
| Type | Description |
|---|---|
IngestionError
|
When nothing could be loaded, or a stage fails. |
Performance
Loading, embedding and indexing are all batched and concurrent. Re-ingesting unchanged content is cheap: chunk ids are content hashes, so the store upserts rather than duplicating, and a configured embedding cache skips the model entirely.
Example
import asyncio, tempfile, pathlib from windlass import Windlass folder = pathlib.Path(tempfile.mkdtemp()) _ = (folder / "a.txt").write_text("Retrieval augmented generation.") asyncio.run(Windlass.rag().aingest(folder)) >= 1 True
Source code in src\windlass\rag\pipeline.py
ingest
¶
ingest(
source: Any, *, metadata: dict[str, Any] | None = None, show_progress: bool = False
) -> int
Blocking :meth:aingest.
aingest_documents
async
¶
Ingest already-loaded documents, skipping the loader.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
documents
|
Sequence[Document]
|
Documents to preprocess, chunk, embed and index. |
required |
Returns:
| Type | Description |
|---|---|
int
|
How many chunks were indexed. |
Source code in src\windlass\rag\pipeline.py
ingest_documents
¶
aingest_text
async
¶
aingest_text(
text: str, *, source: str | None = None, metadata: dict[str, Any] | None = None
) -> int
Ingest a raw string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The text to index. |
required |
source
|
str | None
|
Label recorded as the document's source, used for citation. |
None
|
metadata
|
dict[str, Any] | None
|
Extra metadata. |
None
|
Returns:
| Type | Description |
|---|---|
int
|
How many chunks were indexed. |
Example
import asyncio from windlass import Windlass asyncio.run(Windlass.rag().aingest_text("Some knowledge.")) >= 1 True
Source code in src\windlass\rag\pipeline.py
ingest_text
¶
ingest_text(
text: str, *, source: str | None = None, metadata: dict[str, Any] | None = None
) -> int
Blocking :meth:aingest_text.
asearch
async
¶
asearch(
query: str, k: int | None = None, *, filters: MetadataFilter | None = None
) -> SearchResult
Retrieve without generating an answer.
Useful for building a search UI, for debugging why an answer was wrong, and for evaluating retrieval quality independently of generation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The search query. |
required |
k
|
int | None
|
Override for :attr: |
None
|
filters
|
MetadataFilter | None
|
Metadata constraints. |
None
|
Returns:
| Type | Description |
|---|---|
SearchResult
|
The ranked results. |
Source code in src\windlass\rag\pipeline.py
search
¶
aask
async
¶
aask(
question: str,
*,
k: int | None = None,
filters: MetadataFilter | None = None,
thread_id: str = "default",
**llm_kwargs: Any
) -> RAGAnswer
Answer a question from the indexed corpus.
The full path: input guardrail, retrieval, context assembly, generation, output guardrail, memory update.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
question
|
str
|
The user's question. |
required |
k
|
int | None
|
Override for :attr: |
None
|
filters
|
MetadataFilter | None
|
Metadata constraints on retrieval. |
None
|
thread_id
|
str
|
Conversation thread, when memory is configured. |
'default'
|
**llm_kwargs
|
Any
|
Per-call model overrides ( |
{}
|
Returns:
| Type | Description |
|---|---|
RAGAnswer
|
The answer, the contexts it was based on, token usage and latency. |
Raises:
| Type | Description |
|---|---|
GuardrailViolation
|
When a guardrail blocks the question or answer. |
PipelineError
|
When generation fails. |
Performance
One retrieval (embedding + store query) plus one model call. Context
is truncated to :attr:max_context_tokens from the tail, so a large
top_k never blows the context window.
Example
import asyncio from windlass import Windlass rag = Windlass.rag() _ = rag.ingest_text("The Eiffel Tower is in Paris.") asyncio.run(rag.aask("Where is the Eiffel Tower?")).question 'Where is the Eiffel Tower?'
Source code in src\windlass\rag\pipeline.py
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | |
ask
¶
ask(
question: str,
*,
k: int | None = None,
filters: MetadataFilter | None = None,
thread_id: str = "default",
**llm_kwargs: Any
) -> RAGAnswer
Blocking :meth:aask.
Source code in src\windlass\rag\pipeline.py
astream_ask
async
¶
astream_ask(
question: str,
*,
k: int | None = None,
filters: MetadataFilter | None = None,
thread_id: str = "default",
**llm_kwargs: Any
) -> AsyncIterator[str]
Stream an answer token by token.
Retrieval completes first (it must — the context shapes the prompt), then
generation streams. Guardrails run on the assembled answer after the
stream finishes, so a blocking output policy cannot un-send text that has
already reached the user; use redact semantics or non-streaming
generation when output blocking must be authoritative.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
question
|
str
|
The user's question. |
required |
k
|
int | None
|
Override for :attr: |
None
|
filters
|
MetadataFilter | None
|
Metadata constraints. |
None
|
thread_id
|
str
|
Conversation thread. |
'default'
|
**llm_kwargs
|
Any
|
Per-call model overrides. |
{}
|
Yields:
| Type | Description |
|---|---|
AsyncIterator[str]
|
Text fragments as the model produces them. |
Source code in src\windlass\rag\pipeline.py
stream_ask
¶
Blocking :meth:astream_ask.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
question
|
str
|
The user's question. |
required |
**kwargs
|
Any
|
Forwarded to :meth: |
{}
|
Yields:
| Type | Description |
|---|---|
str
|
Text fragments. |
Source code in src\windlass\rag\pipeline.py
aevaluate
async
¶
aevaluate(
questions: Sequence[str] | Sequence[dict[str, Any]],
*,
evaluator: Any = None,
metrics: Sequence[str] | None = None
) -> Any
Answer a question set and score the results.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
questions
|
Sequence[str] | Sequence[dict[str, Any]]
|
Question strings, or dicts with |
required |
evaluator
|
Any
|
An :class: |
None
|
metrics
|
Sequence[str] | None
|
Metric names, passed to the default evaluator. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
An |
Any
|
class: |
Example
import asyncio from windlass import Windlass rag = Windlass.rag() _ = rag.ingest_text("Paris is the capital of France.") report = asyncio.run(rag.aevaluate( ... [{"question": "Capital of France?", "reference": "Paris"}], ... metrics=["answer_relevancy_lexical"], ... )) report.samples 1
Source code in src\windlass\rag\pipeline.py
evaluate
¶
evaluate(
questions: Sequence[str] | Sequence[dict[str, Any]],
*,
evaluator: Any = None,
metrics: Sequence[str] | None = None
) -> Any
Blocking :meth:aevaluate.
Source code in src\windlass\rag\pipeline.py
native_llm
¶
native_store
¶
native_retriever
¶
describe
¶
Return a JSON-safe description of every wired component.
Useful for logging what a deployment is actually running, and for diffing two environments that behave differently.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A dict keyed by component role. |
Source code in src\windlass\rag\pipeline.py
acount
async
¶
Return how many chunks are indexed.
Reads the vector store, falling back to the retriever's own index for purely lexical pipelines where no vectors are stored.
Source code in src\windlass\rag\pipeline.py
count
¶
aclose
async
¶
Release every component's resources.
Source code in src\windlass\rag\pipeline.py
close
¶
save
¶
Persist the index to disk, when the store supports it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
str | Path
|
Where to write. Created if absent. |
required |
Returns:
| Type | Description |
|---|---|
Path
|
The directory written to. |
Raises:
| Type | Description |
|---|---|
PipelineError
|
When the configured store cannot persist. |
Example
import tempfile from windlass import Windlass rag = Windlass.rag() _ = rag.ingest_text("something worth keeping") rag.save(tempfile.mkdtemp()).is_dir() True
Source code in src\windlass\rag\pipeline.py
load
¶
Restore an index previously written by :meth:save.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
str | Path
|
Directory containing the saved index. |
required |
Returns:
| Type | Description |
|---|---|
RAGPipeline
|
|
Raises:
| Type | Description |
|---|---|
PipelineError
|
When the configured store cannot load. |