Compare

Kitaru vs Inngest: The runtime layer underneath your Python agents

Inngest is a broad event-driven workflow platform spanning TypeScript, Python, and Go. Kitaru is the self-hosted Python runtime that slots underneath the agent harness you already picked, with LLM, wait, memory, and artifact lineage in the box.

pip install kitaru
Book a demo Read the docs

Inngest makes the pitch: “make any code durable across an event-driven app,” and they genuinely do that. One platform 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 isolated branch environments for non-production branches, plus a single-binary self-host path, and you can see why a lot of app teams on Vercel or Lambda picked it. So if your durability problem is shaped like an event-driven app, Inngest is the pragmatic answer.

Kitaru is narrower on purpose. We built it for Python agents, because that’s the workload most teams we talk to are trying to ship right now. Today, it’s increasingly obvious that every company running agents in production needs the same capabilities: a durable llm() call, wait/resume, typed versioned memory, artifact lineage across runs, human/agent approval gates, and a runtime that understands the cloud underneath it. Those don’t come in the box with a general-purpose event-and-function platform. So we put them there, underneath whatever harness your team already picked rather than asking you to re-shape the app around a new platform.

Kitaru

Use Kitaru if you are

  • Running Python agents and want `kitaru.llm()`, `kitaru.wait()`, `kitaru.memory`, and artifact lineage as primitives, not glue code your platform team maintains forever
  • Deploying across Kubernetes, AWS, GCP, or Azure (Vertex AI, SageMaker, AzureML) and want an opinionated stack abstraction where one config switches every flow's backend
  • Keeping the agent harness, auth, entitlements, and observability stack you already picked, and adding only the runtime layer underneath them
  • Designing around dynamic agent loops: tool calls, conditional branches, human/agent approval gates, hours-long waits, replay from a specific checkpoint
Alternative

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, not specifically Python agents
  • Leaning on a flow-control suite (concurrency, throttling, rate limiting, debounce, batching, prioritization) as a first-class feature
  • Fine adopting a function-and-event app shape, with managed Inngest Cloud or a single-binary self-host
Inngest is the durable execution platform you adopt. Kitaru is the runtime layer underneath the harness you already picked.

Runtime under your harness vs platform across your stack

Defaults tell you what a tool is actually for. Inngest’s defaults are workflows, background jobs, agents, durable endpoints, cron, and queue replacement: a packaged platform across your stack. Kitaru’s defaults are @flow and @checkpoint on ordinary Python, with kitaru.llm(), kitaru.wait(), and kitaru.memory shipped underneath whatever harness your team already picked. Different opinion about the workload, same commitment to durability.

Runtime under your harness versus platform across your stack. Compares Inngest as a packaged event-driven platform with Kitaru as the runtime layer beneath an existing Python agent harness.
Inngest · platform across the stack Runtime + event bus + dashboard, packaged together
Inngest platform
events · cron · webhooks durable engine · retries · steps flow control · throttle · concurrency dashboard · replay · branch envs
your app re-shaped around it
Adopt the platform, get durability and the rest as one piece.
Kitaru · runtime, one layer of four Owns the runtime, leaves harness and platform untouched
Platform your auth, entitlements, observability
Harness Pydantic AI, OpenAI, Claude, LangGraph, raw Python
Runtime Kitaru: @flow, @checkpoint, llm(), wait(), memory
Model OpenAI, Anthropic, Google, open-weights
Kitaru owns one layer. The rest of your stack stays as you picked it.
  • Layered model: We think about the agent stack as four layers (model, harness, runtime, platform). Kitaru owns one of them, the runtime, and stays out of the other three. Inngest is broader on purpose: it packages an event-driven platform that also acts as the runtime underneath it. It’s the wrong shape if you’ve already chosen your harness, your auth, your observability stack, and your data path, and only need durability under them.
  • Adoption shape: pip install kitaru, add two decorators to ordinary Python, the flow runs in your process. Inngest asks you to define functions that register with the engine, get triggered by events, and connect over serve() (HTTP) or connect() (outbound WebSocket). Both work, but the wedge is whether the runtime sits inside your app or whether your app gets re-shaped around the runtime.

Python-agent primitives vs polyglot durable functions

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. Kitaru’s surface is kitaru.llm(), kitaru.wait(), kitaru.memory, and kitaru.save() / kitaru.load(), the primitives every Python agent team writes on top of a general-purpose workflow engine.

Python agent primitives versus polyglot durable functions. Compares Inngest step primitives across several languages with Kitaru primitives for Python agent workloads.
Inngest · polyglot durable functions Steps register with the engine across TypeScript, Python, Go
TypeScript Python Go Kotlin/Java
step.run durable step, return value persisted
step.sleep timer that releases compute
step.wait_for_event resume on a matching event send
step.invoke call another function across apps
Polyglot, app-shaped. Agent telemetry is glue you write on top.
Kitaru · Python agent primitives The glue every Python agent team writes, shipped in the box
@flow on ordinary Python 3.11+
kitaru.llm() alias-resolved keys · prompt + token + latency per call
kitaru.wait() human or external input, compute released
kitaru.memory typed scopes, versioned across runs
@checkpoint typed versioned artifact in your bucket
Pydantic AI adapter ships in the box; OpenAI / Claude / LangGraph wrap.
  • Languages: Inngest ships SDKs for TypeScript, Python, and Go (with a Kotlin/Java repo). Kitaru is Python 3.11+ only. If you’re running TypeScript-and-Python-and-Go services that need one durability primitive across all of it, Inngest is the right tool.
  • LLM call: kitaru.llm() resolves the model alias, injects the provider key from the configured secret backend, and logs prompt, response, latency, tokens, and resolved model on every call. Inngest’s equivalent is your code inside a step.run. Durable, but the agent-specific telemetry is glue you write on top.
  • HITL: kitaru.wait() suspends execution for human-in-the-loop or external input. Local runs can prompt inline; remote, CI, non-interactive, or MCP runs move to waiting and can be resolved later via the client, CLI, or MCP. Both work; the Kitaru shape collapses to one primitive without committing the app to an event-trigger model.

Customer-controlled data path vs managed cloud + flow control

Both products ship a durable runtime. The wedge is where the data lives, and how much packaging arrives with it.

Customer-controlled data path versus managed cloud. Compares Inngest Cloud as a hosted engine and dashboard with Kitaru self-hosting where artifacts and compute stay in the customer cloud.
Inngest · managed cloud is the easy path Engine, queue, state, dashboard — hosted for you
your app
Inngest Cloud SOC 2 · SAML · HIPAA BAA
durable engine flow control replay UI
Hosted in US AWS databases
Alt: single-binary self-host
Spend buys managed convenience and a flow-control suite.
Kitaru · self-hosted, one stack abstraction Pick a stack; data and compute stay in your cloud
your @flow
Kitaru server metadata · checkpoint state · logs · short-lived creds
your S3 your GCS Azure Blob
Kubernetes Vertex AI SageMaker AzureML
Spend buys internal platform ownership; data stays in your bucket.
  • Kitaru: Self-hosted by default, single-service Kitaru server, and Helm-deployable. Runs on Kubernetes, AWS, GCP, Azure (Vertex AI, SageMaker, AzureML), picked via stacks. Checkpoint outputs and artifacts are written to the stack’s S3, GCS, or Azure Blob storage; the Kitaru server stores execution metadata, checkpoint state, and logs, and brokers temporary credentials for artifact access. You have no mandatory SaaS control plane in the data path. Configure the stack once; every flow gets artifacts in your object store, secrets through the cloud’s provider, and orchestration on the chosen compute.
  • 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.

Versioned deployments + artifact lineage vs event-source steps

Both runtimes persist work. The data models are different, and they’re optimized for different shapes of recovery.

Versioned deployments and artifact lineage versus event-sourced steps. Compares Inngest step memoization and replay tooling with Kitaru typed artifacts, checkpoint lineage, and tag-routed deployment versions.
Inngest · step memoization + replay UI Engine persists step return values; dashboard turns it operational
function run
step.run · research memoized step.run · draft memoized step.wait_for_event · approve
replay button bulk cancel traces · alerts branch envs
Operational tools follow from durable step memoization.
Kitaru · artifacts + versioned deployments Typed artifacts in your bucket, snapshots routed by tag
execution · review_flow v3
@checkpoint research artifact: brief.md (in your S3)
@checkpoint draft artifact: draft.md (typed, versioned)
kitaru.wait · approve resumable from CLI / SDK / MCP
kitaru deploy review_flow
v1 v2 v3 default → v3 canary → v3
Cross-run diff and tag-routed promotion in the box.
  • Kitaru: Every @checkpoint output lands as a typed, versioned artifact in your own bucket, linked to the execution record. Checkpoint outputs and explicit artifacts are persisted and can be browsed or loaded by execution; kitaru.load(exec_id, name) can read an artifact from another execution.
  • Inngest: Step return values get persisted 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 when a bad event poisons a queue, traces showing what each step did, alerts when a function fails, and branch environments per Git branch so you can ship without breaking prod.
  • Deployment evolution: kitaru deploy <path.py:flow_name> creates a saved deployment version automatically (v1, v2, …). Tags like default, canary, and stable route consumers to a saved snapshot, so promotion and rollback become a tag move rather than a redeploy. The result is that consumers invoke by flow name pinned to whatever version the tag resolves to, and don’t have to ship code when a new agent version ships behind them. We borrowed the shape from how platform teams already manage models in production; the agents on top inherit it for free.

What makes Kitaru unique

Feature Kitaru Inngest
Durable execution and replay Yes Yes
Human/agent-in-the-loop waiting with compute released Yes Yes
Self-hostable (Kitaru: Apache 2.0; Inngest server: SSPL with delayed Apache; SDKs Apache 2.0) Yes Yes
Python-agent-shaped primitives (`kitaru.llm()`, `kitaru.wait()`, `kitaru.memory`) Yes Not supported
Built-in LLM primitive with alias-resolved secrets and per-call token/latency logging Yes Not supported
Typed, versioned artifact lineage per checkpoint with cross-run diff Yes Not supported
Opinionated stack abstraction for Kubernetes, AWS, GCP, Azure Yes Not supported
Immutable versioned deployment snapshots with tag-routed rollout/rollback Yes Not supported
Self-hosted by default with no mandatory SaaS control plane in the data path Yes Not supported
Polyglot SDKs (TypeScript, Python, Go, +Kotlin/Java repo) Not supported Yes
Built-in event triggers, cron, and webhook ingestion Not supported Yes
Built-in flow control: concurrency, throttling, rate limiting, debounce, batching, prioritization Not supported Yes
Managed cloud with SOC 2 Type II, SAML, HIPAA BAA available, branch environments Not supported Yes

How the two surfaces map

ConceptInngestKitaru
Workflow boundary@inngest_client.create_function registered with the engine, triggered by an event@flow on ordinary Python, called as flow.run(...)
Durable stepawait ctx.step.run("name", fn) memoizes the return value in the engine@checkpoint persists a typed, versioned artifact in your own bucket
Determinism / replay safetySide effects that need replay protection should live inside step.run, so the engine can memoize the result@flow and @checkpoint stay plain Python, but replay-safe external side effects still need durable boundaries or idempotency
Pause and resumeawait ctx.step.wait_for_event("name", event="...") plus an external send to resolve itkitaru.wait(name="...", schema=...) releases compute, resumes from any input source
Cross-run stateRoll your own (Redis, Postgres, KV)kitaru.memory: typed, scoped, versioned KV in the box
LLM callYour code inside step.run; provider key, prompt and token logging are glue you writekitaru.llm() resolves the model alias, injects the provider key, logs prompt/tokens/latency per call
Invocationawait inngest_client.send(inngest.Event(name="review.start", data={...})) to send an eventflow.run(...) for source/local execution; saved deployments via kitaru invoke FLOW ..., KitaruClient().deployments.invoke(flow="...", inputs={...}), or flow.invoke(...)
ArtifactsStep return values memoized in the durable engineTyped artifact in your bucket, queryable across runs via kitaru.load(exec_id, name)

Code comparison

Kitaru Recommended
import kitaru
from kitaru import checkpoint, flow

@checkpoint
def research(topic: str) -> str:
  return kitaru.llm(
      prompt=f"Research: {topic}. Return a brief.",
      model="fast",
  )

@checkpoint
def draft(brief: str) -> str:
  return kitaru.llm(
      prompt=f"Write a draft from this brief:\n{brief}",
      model="fast",
  )

@flow
def review_flow(topic: str) -> str:
  brief = research(topic)
  text = draft(brief)
  approved = kitaru.wait(
      name="approve_draft",
      question="Approve draft?",
      schema=bool,
  )
  return text if approved else "Rejected"

review_flow.run(topic="Durable agents").wait()
Inngest (Python SDK)
import datetime
import inngest

inngest_client = inngest.Inngest(app_id="agent-app")

async def call_llm(prompt: str) -> str:
  ...

@inngest_client.create_function(
  fn_id="review-flow",
  trigger=inngest.TriggerEvent(event="review.start"),
)
async def review_flow(ctx: inngest.Context) -> str:
  topic = ctx.event.data["topic"]

  async def research() -> str:
      return await call_llm(f"Research: {topic}")

  async def draft_step(brief: str) -> str:
      return await call_llm(f"Draft: {brief}")

  brief = await ctx.step.run("research", research)
  text = await ctx.step.run("draft", draft_step, brief)

  decision = await ctx.step.wait_for_event(
      "approve_draft",
      event="review.approve",
      timeout=datetime.timedelta(days=1),
  )
  return text if decision and decision.data["ok"] else "Rejected"

# Trigger:  await inngest_client.send(inngest.Event(name="review.start",   data={"topic": "..."}))
# Approve:  await inngest_client.send(inngest.Event(name="review.approve", data={"ok": True}))
# Functions register with the Inngest engine; events route through it.

Durable execution, shaped for Python agents

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 Python agent work specifically (LLM calls, memory, artifacts, human/agent approval gates, a stack abstraction over your own cloud), the glue you’d write on top of a general-purpose event-and-function platform is the stuff we ship in the box.

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.

pip install kitaru
Book a demo