// a new DataFrame primitive
Ondine makes LLM calls a first-class DataFrame operation. Define a column with natural language. Ondine computes it at production scale: checkpointed, capped, grounded against your own data, observable end-to-end.
Architecture
A prompt is a column. Everything else is how to improve the inputs, constrain the outputs, or run production reliably. Ondine's feature set organizes around five dimensions around that primitive.
Usage
Every tab below shows the same primitive in different clothes. Define a column with natural language. Ondine computes it. The rest is inputs (KB, docs) and constraints (schema, grounding).
from ondine import QuickPipeline # 3 lines. Sensible defaults. Checkpointing on by default. pipeline = QuickPipeline.create( data="reviews.csv", # CSV, Parquet, DataFrame prompt="Analyze this review: {review}", # {placeholder} = column output_columns=["sentiment", "score", "key_topic"], # JSON parsed, typed model="gpt-5.4-mini", max_budget=5.00, # hard USD cap ) result = pipeline.execute() # crash at row 80K? re-run. resumes at 80K.
from ondine import PipelineBuilder from pydantic import BaseModel class ReviewAnalysis(BaseModel): sentiment: str score: int topic: str # Full pipeline. Composable stages, total control. pipeline = ( PipelineBuilder.create() .from_csv("reviews.csv", input_columns=["review"], output_columns=["sentiment", "score", "topic"]) .with_prompt("Analyze the review: {review}") .with_llm(provider="openai", model="gpt-5.4-mini") .with_structured_output(ReviewAnalysis) .with_batch_size(50) # 200 calls, not 10,000 .with_max_budget(25.00) # hard halt at $25 .with_checkpoint_interval(100) # checkpoint every 100 rows .with_disk_cache(".cache") # identical prompts = $0 second call .with_router(strategy="latency") # fastest provider wins .build() ) result = pipeline.execute()
from ondine import PipelineBuilder from ondine.knowledge import KnowledgeStore # Index your docs once. Hybrid BM25 + dense search, optional reranker. kb = KnowledgeStore("knowledge.db") kb.ingest("docs/") # PDFs, Markdown, HTML, text — OCR included # Retrieval stage runs before the LLM, injects {_kb_context}. pipeline = ( PipelineBuilder.create() .from_csv("questions.csv", input_columns=["question"], output_columns=["answer"]) .with_knowledge_base(kb, top_k=5, rerank=True, # cross-encoder reranker query_transform="hyde") # or "multi-query", "step-back" .with_prompt("Context:\n{_kb_context}\n\nQ: {question}\nA:") .with_llm(model="gpt-5.4-mini") .build() ) result = pipeline.execute()
from ondine import PipelineBuilder from ondine.context import RustContextStore # Optional trust layer: LLM answers verified against your evidence graph. pipeline = ( PipelineBuilder.create() .from_csv("records.csv", input_columns=["record"], output_columns=["extracted"]) .with_prompt("Extract: {record}") .with_llm(model="gpt-5.4-mini") .with_context_store(RustContextStore("evidence.db")) .with_grounding(threshold=0.3) # contradictions flagged, not written .with_evidence_priming(top_k=3) # cross-run consistency .build() ) result = pipeline.execute() # Rows that failed grounding are marked in result.dataframe.
What "a prompt is a column" unlocks
The use case lives in the prompt. Ondine doesn't care what you're computing. `Prompt(columns) → new_columns` covers all of this, one syntax.
Get started
Pandas and Polars. Any LLM through LiteLLM. Any data workflow you already have. Five dimensions of production support around one primitive.