Inngest and Kitaru solve different problems at different layers. Inngest is a durable execution platform: one engine takes your TypeScript, Python, or Go function, wraps it in retries and step memoization, and gives you the flow-control surface (concurrency, throttling, rate limiting, debounce, batching, prioritization) that most workflow engines leave you to wire up yourself. Add managed Inngest Cloud with SOC 2 Type II, enterprise SAML, HIPAA BAA available, and a single-binary self-host path, and you can see why a lot of teams on Vercel or Lambda picked it. If your durability problem is shaped like an event-driven app, Inngest is the pragmatic answer.
Kitaru doesn’t run your workloads at all — it’s a replay-based eval layer. It records a production run as a session — every model call, tool call, decision — and a session re-executes: unchanged, it reproduces the original as a faithful baseline; forked, it re-runs with one thing changed. Agents running on Inngest are no exception. Wrap them with a first-class adapter (Pydantic AI, OpenAI Agents SDK, LangGraph, Mastra, Vercel AI SDK), or import the traces you already collect in Langfuse, LangSmith, Braintrust, or OTel, and every run becomes a session you can replay.
Use Kitaru if you are
- Running agents in production, on Inngest or anywhere else, and want to test a change before it ships, not just watch logs after
- Recording production runs as sessions via an adapter (Pydantic AI, OpenAI Agents SDK, LangGraph, Mastra, Vercel AI SDK) or importing traces you already collect
- Turning the sessions that caught a failure into a cohort that gates every future change
- Comparing a model or prompt swap on the same frozen population instead of a vibe check
- Self-hosting eval infrastructure so traces and credentials stay in your own systems
Use Inngest if you are
- Running a polyglot estate (TypeScript, Python, Go) that needs one durability primitive across all of it
- Building event-driven workflows, background jobs, durable endpoints, or replacing a queue
- Leaning on a flow-control suite (concurrency, throttling, rate limiting, debounce, batching, prioritization) as a first-class feature
- Fine adopting managed Inngest Cloud, or self-hosting the single-binary engine
Inngest keeps your agent running in production. Kitaru replays what it did, so you can prove a change before it ships.
Different jobs, not one stack
Inngest’s defaults are events, cron, retries, step memoization, and flow control — a packaged platform for making any function durable. Kitaru’s defaults are record, replay, and improve: wrap or import a run as a session, re-execute it with the recording answering for the world, and compare cohorts before and after a change.
- Different problems, not competing solutions. Inngest makes your production code durable. Kitaru makes your production history runnable as a test bench. Neither one does the other’s job.
- Composable by design. An agent step running inside an Inngest function is exactly the kind of run Kitaru wants to record — wrap it with an adapter, or import the trace, and it’s a session like any other.
Durable steps vs recorded sessions
The shape of the SDK tells you who the team was watching when they set the defaults. Inngest’s surface is step.run, step.sleep, step.wait_for_event, step.send_event, step.invoke — durable primitives for any function. Kitaru’s surface is the adapter that wraps your agent, and the client that turns its sessions into a test suite.
- Languages: Inngest ships SDKs for TypeScript, Python, and Go (with a Kotlin/Java repo). Kitaru ships a Python SDK, a TypeScript SDK, a CLI, and an MCP server for your coding agent.
- Recording:
KitaruAgent(agent, agent_id=AGENT_ID)wraps a Pydantic AI agent transparently —run,run_sync,iterbehave exactly as before, and every call lands as a session. - Testing a change:
client.cohorts.create(...)freezes the sessions that matter;client.experiments.create(...)moves one variable (model, prompt, tool policy);client.compare(before, after)shows what changed.
Self-hosted, in your systems
Both products take data seriously. The wedge is where it lives, and how much packaging arrives with it.
- Kitaru: Self-hosted by default — one FastAPI + Postgres server, Apache 2.0. Workers execute replays, imports, and evaluations in your own environment. Traces and credentials don’t leave your systems.
- Inngest: Managed Inngest Cloud is the easy path: SOC 2 Type II, enterprise SAML, HIPAA BAA available, encrypted data at rest and in transit, and isolated production, branch, custom, and local environments. Self-hosting is supported via a single-binary engine. Managed-cloud data is hosted in US AWS databases, which is a constraint for some.
Step memoization vs session replay
Both products persist work so you don’t lose it. The data models are built for different jobs.
- Inngest: Step return values get memoized by the durable engine, so a crashed run replays from the last completed step rather than from the top. The dashboard turns that durability into operational tools: a replay button on stuck functions, bulk cancellation, traces, alerts, and branch environments per Git branch.
- Kitaru: Every recorded call — model or tool — lands as a node on the session: inputs, outputs, tokens, cost, latency. A session replays with tool calls answered from the recording, so nothing touches real systems. Freeze the sessions that matter into a cohort, and an experiment replays that cohort with one variable moved — then
client.compare(before, after)shows what improved and what regressed.
What makes Kitaru unique
| Feature | Kitaru | Inngest |
|---|---|---|
| Durable execution and replay of production workloads | Not supported | Yes |
| Retries, step memoization, and flow control (concurrency, throttling, rate limits) | Not supported | Yes |
| SDKs across TypeScript, Python, and Go (plus a Kotlin/Java repo) | Not supported | Yes |
| Managed cloud with SOC 2 Type II, SAML, HIPAA BAA available | Not supported | Yes |
| Record production agent runs as replayable sessions | Yes | Not supported |
| Adapters for Pydantic AI, OpenAI Agents SDK, LangGraph, Mastra, Vercel AI SDK | Yes | Not supported |
| Import traces from Langfuse, LangSmith, Braintrust, or OTel | Yes | Not supported |
| Replay with tool calls answered from the recording — nothing touches real systems | Yes | Not supported |
| Cohorts: frozen, immutable session sets | Yes | Not supported |
| Experiments: same cohort, one variable moved, compared | Yes | Not supported |
| Deterministic and model-graded evaluators | Yes | Not supported |
| Self-hostable (Kitaru: Apache 2.0 server and workers; Inngest server: SSPL with delayed Apache, SDKs Apache 2.0) | Yes | Yes |
How the two surfaces map
| Concept | Inngest | Kitaru |
|---|---|---|
| Layer | Durable execution platform (how the work runs) | Replay-based evals (how you test what it did) |
| Core unit | Function run | Session — recorded model and tool calls, re-executable |
| Composition | Function registered with the engine, triggered by an event | Agent wrapped once by an adapter, or a trace imported |
| Tool calls in tests | Hit real systems, or you mock them by hand | Answered from the recording per tool policy |
| “Did my change help?” | Read the traces, compare by eye | Two runs over the same cohort, diffed |
| Crash mid-run | Step memoized, engine resumes from there | Partial session recorded — the evidence survives |
| Where it runs | Inngest Cloud, or a self-hosted single binary | Self-hosted FastAPI + Postgres server; workers replay in your environment |
Code comparison
from pydantic_ai import Agent
from kitaru_pydantic_ai import KitaruAgent
agent = Agent("openai:gpt-5.4", name="support-agent",
system_prompt="You resolve support tickets.")
support = KitaruAgent(agent, agent_id=AGENT_ID)
# Runs inside your Inngest step exactly as before —
# and lands as a session.
result = support.run_sync(ticket_text)
# Later: freeze the sessions that matter, test a change.
import kitaru
client = kitaru.KitaruClient()
cohort = client.cohorts.create("hard-cases", sessions=session_ids)
experiment = client.experiments.create("cheap-model", model="gpt-5-mini")
before = experiment.run(cohort=cohort, version="v1")
after = experiment.run(cohort=cohort, version="pr-311")
client.compare(before, after)import inngest
inngest_client = inngest.Inngest(app_id="agent-app")
async def call_llm(prompt: str) -> str:
...
@inngest_client.create_function(
fn_id="support-flow",
trigger=inngest.TriggerEvent(event="ticket.created"),
)
async def support_flow(ctx: inngest.Context) -> str:
ticket = ctx.event.data["ticket"]
async def triage() -> str:
return await call_llm(f"Triage: {ticket}")
result = await ctx.step.run("triage", triage)
return result
# Trigger: await inngest_client.send(
# inngest.Event(name="ticket.created", data={"ticket": "..."}))
# Functions register with the Inngest engine; events route through it.Record what Inngest already runs
If your durability problem is shaped like an event-driven app — workflows triggered by events, cron jobs, durable endpoints, queue replacement — Inngest is the right tool, and I’d tell any team that. For testing whether a change to your agent actually helped — replay the sessions that already happened, fork them with one variable moved, compare before and after — Kitaru is the layer that sits beside it, not underneath it.
We’ve spent five years building the MLOps-ready version of this problem space at ZenML. JetBrains runs their AI globally on it; Adeo runs across all their brands and geographies on it. Kitaru is that team two years into the agent version. Bet on us for agent infrastructure and you’re betting on the group that’s been doing this the whole time.
uv add "kitaru[cli,worker]" kitaru-pydantic-ai