Skip to content

RAG, auto-tune and saved presets

RAG and auto-tune are part of the shared surface on both tiers. Pro requires a valid product licence; these features have no separate licence entitlements. Operators can disable them with PRO_RAG_ENABLED and PRO_AUTO_TUNE_ENABLED, both enabled by default. Project configuration and the caller’s permissions also determine which operations are available.

A saved preset is a project-scoped, user-authored extraction configuration, distinct from the curated read-only presets presets()/presets/Presets lists. Both tiers serve it at /v1/saved_presets.

Python
created = client.create_saved_preset({"name": "invoices", "extraction_config": {"preset": "invoice"}})
preset = client.get_saved_preset(created.id)
client.update_saved_preset(created.id, {"name": "invoices-v2", "extraction_config": {"preset": "invoice"}})
client.delete_saved_preset(created.id)
TypeScript
const created = await client.createSavedPreset({ name: "invoices", extraction_config: { preset: "invoice" } });
const preset = await client.getSavedPreset(created.id);
await client.deleteSavedPreset(created.id);
Go
created, err := client.CreateSavedPreset(ctx, xberg.CreateSavedPresetRequest{Name: "invoices"})
preset, err := client.GetSavedPreset(ctx, created.Id.String())
err = client.DeleteSavedPreset(ctx, created.Id.String())

Reference a saved preset from an extraction with options.saved_preset_id; it is mutually exclusive with options.preset (a curated preset id).

Auto-tune searches for an extraction configuration that best matches ground truth you supply, across a set of sample documents. Submit is multipart: a JSON request part (the documents and their ground truth, plus optional budget/search/routing) alongside one file part per document, matched by filename.

Python
from pathlib import Path
job = client.submit_auto_tune(
{
"documents": [
{
"filename": "invoice.pdf",
"ground_truth_kind": "structured",
"ground_truth": '{"schema": {...}, "expected": {...}}',
},
],
},
files=[Path("invoice.pdf")],
)
status = client.get_auto_tune_status(job.auto_tune_job_id)
TypeScript
const job = await client.submitAutoTune({
request: {
documents: [
{ filename: "invoice.pdf", ground_truth_kind: "structured", ground_truth: groundTruthJson },
],
},
files: [{ name: "invoice.pdf", data: invoiceBytes, mimeType: "application/pdf" }],
});
const status = await client.getAutoTuneStatus(job.auto_tune_job_id);
Go
job, err := client.SubmitAutoTune(ctx, xberg.CreateAutoTuneJobRequest{
Documents: []xberg.AutoTuneDocumentInput{
{Filename: "invoice.pdf", GroundTruthKind: xberg.GroundTruthKind("structured"), GroundTruth: groundTruthJSON},
},
}, []xberg.FileSource{{Name: "invoice.pdf", Reader: invoiceFile}})
status, err := client.GetAutoTuneStatus(ctx, job.AutoTuneJobId)

ground_truth_kind is one of markdown, plaintext or structured. For structured, ground_truth is a JSON string encoding {"schema": ..., "expected": ...} — both the schema and the expected output are required.

Poll with get_auto_tune_status/getAutoTuneStatus/GetAutoTuneStatus until the job completes, then fetch the leaderboard and winning configuration:

Python
result = client.get_auto_tune_result(job.auto_tune_job_id)
profile = client.promote_auto_tune_profile(job.auto_tune_job_id, {"name": "invoices-tuned"})

Promoting writes a named, reusable tuning profile — list and manage those with list_tuning_profiles/get_tuning_profile/delete_tuning_profile (and their TypeScript/Go equivalents). get_auto_tune_capabilities/getAutoTuneCapabilities/GetAutoTuneCapabilities reports which OCR backends and knobs the connected deployment can search over — check it before building a search config the deployment cannot honor.

The RAG surface manages vector collections, ingests documents into them, and retrieves chunks. All three clients return the decoded response body untyped (Any in Python, unknown in TypeScript, json.RawMessage in Go) — the schemas are still converging across tiers, so decode against whichever fields you need.

Python
from xberg_io_sdk.errors import XbergError
try:
client.create_rag_collection({"name": "policies", "embedding_dim": 384})
ingest = client.add_rag_documents(
"policies", {"kind": "text", "external_id": "doc-1", "text": "Refunds are available within 30 days."}
)
except XbergError as exc:
print(f"RAG ingest failed: {exc}")
raise
TypeScript
try {
await client.createRagCollection({ name: "policies", embedding_dim: 384 });
const ingest = await client.addRagDocuments("policies", {
kind: "text", external_id: "doc-1", text: "Refunds are available within 30 days.",
});
} catch (err) {
console.error("RAG ingest failed", err);
throw err;
}
Go
_, err := client.CreateRagCollection(ctx, map[string]any{"name": "policies", "embedding_dim": 384})
if err != nil {
log.Fatal(err)
}
_, err = client.AddRagDocuments(ctx, "policies", map[string]any{
"kind": "text", "external_id": "doc-1", "text": "Refunds are available within 30 days.",
})
if err != nil {
log.Fatal(err)
}

Despite the plural SDK method name, each call submits one document using a flat request body. Text ingest returns HTTP 202 with job_id and document_id; poll that job with get_rag_job / getRagJob / GetRagJob and check successful completion before retrieving chunks or deleting the collection. Text ingest requires a working embedding configuration.

retrieve’s mode defaults to hybrid; pass "vector", "full_text" or a BYO query_vector matching the collection’s embedding_dim. top_k is 1..=200 (default 10); rerank is three-state — omit it for the collection default, set it to null to disable reranking for this call, or supply a RerankerSpec to override it.

Long-running collection operations — ingest, reindex, embedding migration — return a job you poll with get_rag_job/getRagJob/GetRagJob (or, for migrations specifically, get_rag_migration_job/getRagMigrationJob/GetRagMigrationJob), the same terminal-status contract as extraction jobs.

Pro also exposes a per-project RAG default via get_rag_config/set_rag_config — see the control plane guide.