Full setup guide. Define your goals, pick your models, tell Kalibr what success looks like, and let the Router learn which path works best. If you just want to see it work first, start with the Quickstart.
Your Tenant ID is assigned when you create an account. Create an API key separately in Settings > API Keys. The key is only shown once. Copy it before closing the dialog.
pip install kalibr openai anthropic
KALIBR_API_KEY=sk_... KALIBR_TENANT_ID=user_... OPENAI_API_KEY=sk-... ANTHROPIC_API_KEY=sk-ant-...
import kalibr must be the first import in the file where your LLM client is created. This is how the SDK patches the provider clients at import time.
import kalibr # must be first import
from kalibr import Router
router = Router(
goal="summarize",
paths=["gpt-4o", "claude-sonnet-4-20250514", "deepseek-chat"],
success_when=lambda out: len(out) > 50
)
response = router.completion(
messages=[{"role": "user", "content": "Summarize: ..."}]
)
print(response.choices[0].message.content)
# router.report() is called automatically when success_when is setManual reporting when your validation is too complex for a lambda:
router = Router(goal="book_meeting", paths=["gpt-4o", "claude-sonnet-4-20250514"]) response = router.completion(messages=[...]) # your validation logic router.report(success=meeting_was_booked)
Continuous scoring for finer-grained quality signals (0.0 to 1.0):
router = Router(
goal="draft_email",
paths=["gpt-4o", "claude-sonnet-4-20250514"],
score_when=lambda out: min(1.0, len(out) / 800)
)npm install @kalibr/sdk openai
KALIBR_API_KEY=sk_... KALIBR_TENANT_ID=user_... OPENAI_API_KEY=sk-...
import kalibr from "@kalibr/sdk";
import { Router } from "@kalibr/sdk";
const router = new Router({
goal: "summarize",
paths: ["gpt-4o", "claude-sonnet-4-20250514"],
successWhen: (out) => out.length > 50,
});
const response = await router.completion([
{ role: "user", content: "Summarize: ..." }
]);
console.log(response.choices[0].message.content);pip install kalibr[langchain] langchain langchain-openai langchain-anthropic
KALIBR_API_KEY=sk_... KALIBR_TENANT_ID=user_... OPENAI_API_KEY=sk-... ANTHROPIC_API_KEY=sk-ant-...
router.as_langchain() returns a drop-in LangChain LLM. Use it anywhere you would use ChatOpenAI or ChatAnthropic.
from kalibr import Router
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
router = Router(goal="summarize_text", paths=["gpt-4o-mini", "claude-sonnet-4-20250514"])
chain = (
ChatPromptTemplate.from_template("Summarize: {text}")
| router.as_langchain()
| StrOutputParser()
)
result = chain.invoke({"text": "Your document..."})
router.report(success=len(result) > 50)pip install kalibr[crewai] crewai openai anthropic
Requires Python 3.10 or higher.
KALIBR_API_KEY=sk_... KALIBR_TENANT_ID=user_... OPENAI_API_KEY=sk-... ANTHROPIC_API_KEY=sk-ant-...
One change. Pass the Kalibr router as the LLM for your CrewAI agent.
from kalibr import Router
from crewai import Agent, Task, Crew
router = Router(goal="research_task", paths=["gpt-4o-mini", "claude-sonnet-4-20250514"])
researcher = Agent(
role="Researcher", goal="Find accurate information",
backstory="Research assistant.",
llm=router.as_langchain(),
)
task = Task(description="...", expected_output="...", agent=researcher)
result = Crew(agents=[researcher], tasks=[task]).kickoff()
router.report(success=len(str(result)) > 100)pip install kalibr[openai-agents] openai-agents
KALIBR_API_KEY=sk_... KALIBR_TENANT_ID=user_... OPENAI_API_KEY=sk-...
get_policy() returns the current recommended model for a goal. Pass it directly to your Agent.
from kalibr import Router, get_policy from agents import Agent, Runner router = Router(goal="agent_task", paths=["gpt-4o", "gpt-4o-mini"]) policy = get_policy(goal="agent_task") agent = Agent(name="Assistant", instructions="...", model=policy["recommended_model"]) result = Runner.run_sync(agent, "Your task") router.report(success=len(result.final_output) > 0)
pip install kalibr huggingface_hub
KALIBR_API_KEY=sk_... KALIBR_TENANT_ID=user_... HF_API_TOKEN=hf_... # optional, avoids free-tier rate limits
All 17 HuggingFace task types are supported. HuggingFace is not auto-patched on import kalibr. Use router.completion() directly or call kalibr.auto_instrument(["huggingface"]) explicitly.
import kalibr
kalibr.auto_instrument(["huggingface"])
from kalibr import Router
router = Router(
goal="transcribe_calls",
paths=["openai/whisper-large-v3", "facebook/seamless-m4t-v2-large"],
success_when=lambda out: len(out) > 50
)
result = router.execute(task="automatic_speech_recognition", input_data=audio_bytes)
router.report(success=True)pip install kalibr openai # DeepSeek uses the OpenAI SDK
KALIBR_API_KEY=sk_... KALIBR_TENANT_ID=user_... DEEPSEEK_API_KEY=sk-... # from platform.deepseek.com
Use deepseek-chat and deepseek-reasoner as path names. Mix with other providers freely.
import kalibr # must be first import
from kalibr import Router
router = Router(
goal="classify_icp",
paths=["gpt-4o-mini", "deepseek-chat", "claude-sonnet-4-20250514"],
success_when=lambda out: len(out) > 0
)
response = router.completion(messages=[{"role": "user", "content": "..."}])
print(response.choices[0].message.content)pip install kalibr[voice] # ElevenLabs + Deepgram + OpenAI Audio
KALIBR_API_KEY=sk_... KALIBR_TENANT_ID=user_... OPENAI_API_KEY=sk-... ELEVENLABS_API_KEY=... DEEPGRAM_API_KEY=...
import kalibr # patches ElevenLabs + Deepgram
from kalibr import Router
tts = Router(goal="narrate", paths=["eleven_multilingual_v2", "tts-1"])
audio = tts.synthesize("Hello from Kalibr.", voice="alloy").audio
stt = Router(goal="transcribe_calls", paths=["whisper-1", "nova-2"])
transcript = stt.transcribe(audio_bytes, audio_duration_seconds=30.0).textPass healing=True on router.completion() to let Kalibr recover from failed calls automatically. The Router runs the structural gate, classifies any failure, repairs the meta prompt or swaps to the next path, and retries — all inside the same call.
from kalibr import Router
router = Router(
goal="summarize",
paths=["gpt-4o", "claude-sonnet-4-20250514"],
success_when=lambda out: len(out) > 50,
)
response = router.completion(
messages=[{"role": "user", "content": "Summarize: ..."}],
healing=True,
)Pass HealConfig to tune retry budget and which gates run:
from kalibr import Router, HealConfig
config = HealConfig(
max_retries=2, # heal attempts before giving up
gate2_enabled=True, # LLM-judge quality gate
meta_prompt_enabled=True,# repair meta prompt before model swap
)
response = router.completion(
messages=[{"role": "user", "content": "Summarize: ..."}],
healing=True,
heal_config=config,
)router.pipeline() runs a sequence of routed, gated, healed steps as one call. Each step picks its own goal and messages. Set "chain": True on any step to receive the previous step's output as context.
result = router.pipeline(
[
{"goal": "research", "messages": [...]},
{"goal": "outreach_generation", "messages": [...], "chain": True},
],
healing=True,
pipeline_id="my-pipeline",
)Each step routes and heals independently. If a step fails after exhausting retries, the pipeline returns a partial result with the failure attached.
Pass pipeline_id to scope outcome learning to a single pipeline (or agent). Calls that share a goal but live in different pipeline_ids will not bleed routing signals into each other. Use the same id across calls that should share learning, a different id for anything you want kept separate.
router.completion(
messages=[...],
healing=True,
pipeline_id="sales-outreach-prod",
)
router.completion(
messages=[...],
healing=True,
pipeline_id="sales-outreach-staging", # separate bandit
)Run this after your first call. It confirms the SDK is installed, credentials are valid, and traces are arriving.
kalibr verify
The first successful trace activates Kalibr's routing engine. After 20 to 50 outcomes per path, routing converges on the model that actually works best for each goal.
Kalibr sits between your code and the LLM providers. Every call goes through the Router, which:
success_when or score_when criteriaCalls go directly to the provider. Kalibr is not a proxy. It does not read or modify prompt content.
For the full explanation, see How Kalibr works.