Pipeline Components¶
ai4rag.utils.data and ai4rag.assets_generator provide reusable building blocks for RAG pipeline workflows. These functions encapsulate the business logic that was previously inlined in Kubeflow Pipeline components, making it available for use in any context — KFP pipelines, standalone scripts, notebooks, or tests.
Architecture¶
┌──────────────────────────────────────────────┐
│ pipelines-components (KFP wrappers + │
│ RAG optimization orchestration) │
│ ┌─────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ @dsl. │ │ @dsl. │ │ @dsl. │ │
│ │component│ │component │ │ component │ │
│ └────┬────┘ └─────┬────┘ └──────┬───────┘ │
│ │ │ │ │
└───────┼────────────┼─────────────┼───────────┘
│ │ │
┌───────▼────────────▼─────────────▼───────────┐
│ ai4rag (business logic) │
│ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ utils/ │ │
│ │ data/ │ │
│ │ clients/ — s3, maas_client │ │
│ │ assets_generator/ │ │
│ │ notebook, leaderboard, templates │ │
│ └───────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────┐ │
│ │ core/ — experiment, HPO │ │
│ │ search_space/ — search space, prepare │ │
│ └──────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
KFP wrappers handle artifact I/O (reading dsl.Input[Artifact], writing dsl.Output[Artifact]), Kubernetes-specific concerns (secrets, resource limits), and full RAG-optimization orchestration. Data-stage and asset-generation business logic lives in ai4rag, so it can be exercised outside KFP.
Installation¶
S3 support (boto3), multiprocessing (multiprocess), and text extraction for born-digital documents (docling-slim[feat-chunking]) are all included in the core ai4rag install. OCR (scanned PDFs/images) and audio transcription additionally require the text-extraction extra — see Installation.
Data Components¶
Document Discovery¶
List and sample documents from one or more locations in an S3-compatible bucket:
from ai4rag.utils.data import discover_documents
result = discover_documents(
bucket_name="my-bucket",
prefixes=["documents/", "manuals/"],
sampling_enabled=True,
sampling_max_size_gb=1.0,
)
print(f"Found {result.count} documents ({result.total_size_bytes} bytes)")
result.save("/tmp/discovery_output")
Every prefix is listed and merged into a single corpus deduplicated by object key, so overlapping selections (docs/ and docs/manuals/) are safe. The sampling budget applies to that union, not to each location. Omitting prefixes — or passing an empty list — lists the whole bucket; a bare string is accepted as a single location.
When test_data_doc_names is given, every entry must identify exactly one discovered document. A key that matches nothing, or a bare file name shared by documents in two locations, raises BenchmarkKeyError naming the offending keys rather than leaving the question silently ungrounded:
from ai4rag.utils.data import BenchmarkKeyError, discover_documents
try:
result = discover_documents(
bucket_name="my-bucket",
prefixes=["documents/"],
test_data_doc_names=["documents/report.pdf"],
)
except BenchmarkKeyError as exc:
print(exc) # names the keys and the locations that were searched
Text Extraction¶
Download documents from S3 and extract text using Docling:
from ai4rag.utils.data import extract_text
result = extract_text(
documents=[{"key": "docs/report.pdf", "size_bytes": 1024}],
bucket="my-bucket",
output_dir="/tmp/extracted",
max_extraction_workers=4,
)
print(f"Processed {result.processed_count}/{result.total_documents}")
Supported document extensions include PDF, DOCX, PPTX, Markdown, HTML, TXT, ODT/ODP, AsciiDoc, LaTeX, EPUB, email (.eml, .msg), Quarto/R Markdown, XHTML, images (JPEG, PNG, TIFF), and audio (WAV, MP3, M4A, AAC, OGG, FLAC).
OCR is off by default. Conversion behaviour (table structure and OCR) is controlled through a single DoclingExtractionConfig instance. To enable RapidOCR (for scanned PDFs / images):
from ai4rag.utils.data import DoclingExtractionConfig, extract_text
result = extract_text(
documents=[{"key": "scans/page.png", "size_bytes": 2048}],
bucket="my-bucket",
output_dir="/tmp/extracted",
docling_config=DoclingExtractionConfig(
do_ocr=True, # RapidOCR via Docling
ocr_lang="english", # default when OCR is enabled
# Optional custom ONNX models for disconnected / specialized deployments:
# ocr_det_model_path="/models/det.onnx",
# ocr_cls_model_path="/models/cls.onnx",
# ocr_rec_model_path="/models/rec.onnx",
# ocr_rec_keys_path="/models/keys.txt",
),
)
Language handling. There is no automatic language detection — ocr_lang selects which bundled RapidOCR model set is loaded. Latin-script languages all resolve to the English models; only Chinese switches to the dedicated Chinese models. ocr_lang accepts a single string ("english") or a sequence (["english", "chinese"]) and defaults to ["english"] when OCR is enabled.
Default RapidOCR models are not in current PyPI rapidocr wheels. On AutoRAG/OpenShift images with DOCLING_ARTIFACTS_PATH set, bake ONNX models under $DOCLING_ARTIFACTS_PATH/RapidOcr/ at image build time (see tmp/Containerfile.autorag-dev). Docling auto-detects pages that need OCR when do_ocr=True. Override with ocr_*_model_path for custom ONNX sets.
Audio Transcription¶
Audio files (.wav, .mp3, .m4a, .aac, .ogg, .flac) are transcribed automatically — no configuration flag is needed, unlike OCR. extract_text routes them through Docling's ASR pipeline using a Whisper (whisper-tiny) model, with the spoken language auto-detected per file:
from ai4rag.utils.data import extract_text
result = extract_text(
documents=[{"key": "recordings/call.mp3", "size_bytes": 4096}],
bucket="my-bucket",
output_dir="/tmp/extracted",
)
Test Data Loading¶
Load benchmark test data from S3:
from ai4rag.utils.data import load_test_data
result = load_test_data(
bucket_name="my-bucket",
key="benchmarks/test_data.json",
benchmark_sample_size=25,
)
print(f"Loaded {result.record_count} records (sampled: {result.sampled})")
Search Space Preparation¶
Build and validate a search space, then serialize it to a report. Model pre-selection is a separate step (see ModelsPreSelector); the report written here is the full search space:
from ai4rag.search_space.prepare import (
build_search_space_report,
prepare_search_space_with_maas,
)
search_space = prepare_search_space_with_maas(
payload={
"foundation_models": [{"model_id": "qwen3-8b-fp8-dynamic"}],
"embedding_models": [{"model_id": "bge-m3"}],
"chunking_methods": ["recursive"], # optional: constrain chunking methods
"chunk_sizes": [256, 512, 1024], # optional: constrain chunk sizes
"chunk_overlaps": [0, 128], # optional: constrain chunk overlaps
},
client=client,
benchmark_data=benchmark_df, # optional: used for language detection
)
build_search_space_report(search_space).save_json("/tmp/search_space.json")
RAG optimization orchestration
Running a full optimization experiment (run_rag_optimization) — wiring search-space preparation, indexing, retrieval, generation, and evaluation together into RAG patterns — is a KFP pipeline concern and lives in the pipelines-components repo, on top of the ai4rag.core and ai4rag.search_space APIs.
Shared Utilities¶
ai4rag.utils.clients provides thin, dependency-injectable adapters over the external services pipeline steps talk to, and ai4rag.utils.docling_io loads persisted DoclingDocument JSON:
| Module | Function | Purpose |
|---|---|---|
ai4rag.utils.clients.s3 | create_s3_client() | S3 client factory with env-var fallback |
ai4rag.utils.clients.maas_client | create_maas_client() | Single MaaS client (endpoint from MAAS_BASE_URL, normalized to a /v1-suffixed URL) for listing, chat, and embeddings, with SSL self-signed cert fallback |
ai4rag.utils.docling_io | load_docling_documents() | Load DoclingDocument JSON files |
create_s3_client() and create_maas_client() are also re-exported from the ai4rag.utils.clients package for convenience:
from ai4rag.utils.clients import create_maas_client, create_s3_client
from ai4rag.utils.docling_io import load_docling_documents
Single client for everything
create_maas_client() builds the one client MaaS needs: it lists available models (models.list()) and is reused, unchanged, to serve chat.completions and embeddings for every model wrapper. See Provider-Agnostic Design for the full pattern.
Design Principles¶
- No KFP types: Functions accept plain Python types (
str,Path,dict) and return frozen dataclasses. - Dependency injection: All functions accept pre-configured clients (S3, MaaS) as optional parameters — when omitted, clients are created from environment variables.
- Lazy imports: Heavy optional dependencies (
boto3,multiprocess,docling) are imported only when used. - SSL fallback: S3 operations and the MaaS client automatically retry with
verify=Falsewhen self-signed certificate errors are detected.