Skip to main content

Langrove

Open-source, self-hosted drop-in replacement for LangGraph deployment (LangSmith Deployments).

Works with langgraph_sdk.get_client(), the useStream() React hook, and Agent Chat UI. Agent Protocol compliant.

Why Langrove?

LangGraph Cloud is proprietary and requires LangSmith. Langrove gives you the same API surface — assistants, threads, runs, streaming, store, crons — on your own infrastructure. Deploy anywhere: bare metal, Docker, Kubernetes.

Features

  • Full LangGraph SDK compatibilityget_client() works out of the box
  • SSE streaminguseStream() compatible wire format (messages, values, updates)
  • Background runs — Redis Streams with at-least-once delivery, crash recovery, dead-letter queue
  • Persistent threads — PostgreSQL-backed checkpointing with state history
  • Cross-thread store — Hierarchical key-value storage with namespace search
  • Cron jobs — Schedule recurring graph executions
  • Interrupt/resume — Pause graphs at any node, resume with human input
  • Multitask strategies — reject, interrupt, rollback, enqueue
  • Custom auth — Plug in JWT, API key, or any async auth handler
  • Agent Protocol/agents endpoints for standard agent interop
  • AI-assisted development — Automated planning, implementation, review, and merge via Claude Code

Quickstart

# 1. Install
uv sync

# 2. Start infrastructure
docker compose up postgres redis -d

# 3. Run migrations
uv run langrove migrate

# 4. Initialize a project (creates langgraph.json + agent.py)
uv run langrove init

# 5. Start the server
uv run langrove serve

# 6. Start the background worker (separate terminal)
uv run langrove worker

The API is now running at http://localhost:8123. Use any LangGraph SDK client:

from langgraph_sdk import get_client

client = get_client(url="http://localhost:8123")

# Create a thread and stream a response
thread = await client.threads.create()
async for event in client.runs.stream(
    thread["thread_id"],
    "agent",  # graph_id from langgraph.json
    input={"messages": [{"role": "user", "content": "Hello!"}]},
):
    print(event)

Architecture

┌─────────────┐     ┌──────────────┐     ┌──────────────┐
│   Client     │────▶│  API Server  │────▶│  PostgreSQL  │
│  (SDK/UI)    │◀────│  (FastAPI)   │     │  (threads,   │
└─────────────┘ SSE └──────┬───────┘     │  runs, store,│
                           │             │  checkpoints)│
                           ▼             └──────────────┘
                    ┌──────────────┐
                    │    Redis     │
                    │  (Streams +  │
                    │   Pub/Sub)   │
                    └──────┬───────┘
                           │
                           ▼
                    ┌──────────────┐
                    │   Worker     │
                    │  (graph      │
                    │  execution)  │
                    └──────────────┘
  • API Server (FastAPI/uvicorn) — HTTP endpoints, SSE streaming for foreground runs
  • Worker — Consumes background tasks from Redis Streams, executes LangGraph graphs
  • PostgreSQL — Threads, runs, assistants, store, crons + LangGraph checkpoint tables
  • Redis — Task queue (Streams + consumer groups), live stream relay (Pub/Sub), event replay

Configuration

langgraph.json

{
  "graphs": {
    "agent": "./agent.py:graph"
  },
  "auth": {
    "path": "./auth.py:authenticate",
    "type": "custom"
  },
  "http": {
    "cors": {
      "allow_origins": ["http://localhost:3000"],
      "allow_methods": ["*"],
      "allow_headers": ["*"],
      "allow_credentials": true
    }
  }
}

Environment Variables

All variables use the LANGROVE_ prefix and are optional — every one has a sensible default.

Infrastructure

Variable Default Description
LANGROVE_DATABASE_URL postgresql://langrove:langrove@localhost:5432/langrove asyncpg connection string
LANGROVE_REDIS_URL redis://localhost:6379 Redis URL for queue and pub/sub

Server

Variable Default Description
LANGROVE_HOST 0.0.0.0 Bind address
LANGROVE_PORT 8123 Bind port
LANGROVE_CONFIG_PATH langgraph.json Path to langgraph.json

Connection pools

Variable Default Description
LANGROVE_DB_POOL_MIN_SIZE 2 Min asyncpg connections (app queries)
LANGROVE_DB_POOL_MAX_SIZE 10 Max asyncpg connections (app queries)
LANGROVE_CHECKPOINTER_POOL_MAX_SIZE 5 Max psycopg connections (checkpointer)
LANGROVE_STORE_POOL_MAX_SIZE 5 Max psycopg connections (store)

Worker

Variable Default CLI flag Description
LANGROVE_WORKER_CONCURRENCY 5 -t/--concurrency Concurrent async tasks per worker
LANGROVE_TASK_TIMEOUT_SECONDS 900 --task-timeout Max task execution time (seconds)
LANGROVE_MAX_DELIVERY_ATTEMPTS 3 --max-retries Retries before dead-letter
LANGROVE_SHUTDOWN_TIMEOUT_SECONDS 30 --shutdown-timeout Graceful shutdown wait (seconds)
LANGROVE_QUEUE_NAME langrove -Q/--queues Redis queue name

Events

Variable Default Description
LANGROVE_EVENT_STREAM_TTL_SECONDS 86400 SSE event retention in Redis (24 hours)

Loading order (last wins): system env → langgraph.json env field → CLI flags.

The env field in langgraph.json accepts either a path to a .env file (resolved relative to the config file) or an inline dict:

{
  "env": ".env"
  // or: "env": { "LANGROVE_DATABASE_URL": "postgresql://..." }
}

API Reference

Health & Info

Method Path Description
GET /ok Liveness check
GET /health Health check (database + redis)
GET /info Server info and registered graphs

Assistants

Method Path Description
POST /assistants Create assistant
GET /assistants/{id} Get assistant
PATCH /assistants/{id} Update assistant
DELETE /assistants/{id} Delete assistant
POST /assistants/search Search assistants
GET /assistants/{id}/schemas Get input/output schemas

Agents (Agent Protocol)

Method Path Description
POST /agents/search Search agents
GET /agents/{id} Get agent
GET /agents/{id}/schemas Get agent schemas

Threads

Method Path Description
POST /threads Create thread
GET /threads/{id} Get thread
PATCH /threads/{id} Update thread metadata
DELETE /threads/{id} Delete thread
POST /threads/search Search threads
POST /threads/{id}/copy Copy thread
GET /threads/{id}/state Get thread state
POST /threads/{id}/state Update thread state
GET /threads/{id}/history Get state history

Runs

Method Path Description
POST /runs/stream Stateless streaming run
POST /runs/wait Stateless blocking run
POST /runs Create background run
POST /runs/search Search runs
GET /runs/{id} Get run
POST /runs/{id}/cancel Cancel run
DELETE /runs/{id} Delete run
GET /runs/{id}/stream Join background run's SSE stream
POST /threads/{id}/runs/stream Stream run on thread
POST /threads/{id}/runs/wait Blocking run on thread
POST /threads/{id}/runs Background run on thread
GET /threads/{id}/runs List thread runs
GET /threads/{id}/runs/{rid} Get thread run
GET /threads/{id}/runs/{rid}/stream Join thread run stream
POST /threads/{id}/runs/{rid}/cancel Cancel thread run

Store

Method Path Description
PUT /store/items Put item
GET /store/items Get item by namespace + key
DELETE /store/items Delete item
POST /store/items/search Search items by namespace prefix
POST /store/namespaces List namespaces

Crons

Method Path Description
POST /runs/crons Create cron job
PATCH /runs/crons/{id} Update cron
DELETE /runs/crons/{id} Delete cron
POST /runs/crons/search Search crons
POST /threads/{id}/runs/crons Create thread-bound cron

SSE Wire Format

Compatible with useStream() React hook:

event: metadata
data: {"run_id": "..."}

event: values
data: {full_state_snapshot}

event: messages
data: [message_chunk, metadata]

event: end
data: null

Stream modes: messages (LLM chunks), values (full state after each node), updates (node deltas).

Custom Authentication

Create an auth handler and reference it in langgraph.json:

# auth.py
async def authenticate(headers: dict[str, str]) -> dict:
    token = headers.get("authorization", "").removeprefix("Bearer ")
    if not token:
        raise Exception("Missing token")
    # Validate token (JWT, DB lookup, etc.)
    return {"identity": "user-123", "role": "user"}
{
  "auth": {
    "path": "./auth.py:authenticate"
  }
}

Unauthenticated paths: /ok, /health, /info, /docs, /openapi.json, /redoc.

Background Runs

Background runs use Redis Streams with late acknowledgment for crash resilience:

  1. API publishes task to Redis Stream (XADD)
  2. Worker reads via consumer group (XREADGROUP) — enters Pending Entries List
  3. Worker executes graph, publishes events via Pub/Sub
  4. Worker acknowledges only after success (XACK)
  5. On crash: XAUTOCLAIM reclaims unacked tasks after timeout
  6. Poison messages (>3 attempts): moved to dead-letter stream

Multitask Strategies

When a thread already has an active run:

Strategy Behavior
reject Return 409 Conflict (default)
interrupt Cancel current run, start new
rollback Revert to last checkpoint, start new
enqueue Queue behind current run

Interrupt & Resume

Pause a graph at any node for human-in-the-loop:

# Start a run that pauses before "review" node
async for event in client.runs.stream(
    thread_id, "agent",
    input={"messages": [{"role": "user", "content": "Draft a report"}]},
    interrupt_before=["review"],
):
    print(event)

# Resume with human feedback
async for event in client.runs.stream(
    thread_id, "agent",
    command={"resume": True},
    input={"messages": [{"role": "user", "content": "Approved, publish it"}]},
):
    print(event)

Docker Deployment

# Full stack
docker compose up

# Or just infrastructure (for local development)
docker compose up postgres redis -d

The docker-compose.yml includes API server, worker, PostgreSQL (pgvector), and Redis.

CLI Reference

langrove serve [--host 0.0.0.0] [--port 8123] [--reload]
langrove worker [--worker-id ID] [-t CONCURRENCY] [--task-timeout SECS] [--max-retries N] [--shutdown-timeout SECS] [-Q QUEUE]
langrove init [--template chatbot]

Examples

Example What it demonstrates
examples/quickstart/ Basic setup, streaming, threads, SDK client
examples/custom-auth/ JWT/API key auth handler, authenticated client
examples/multi-agent-store/ Multiple graphs, store API, crons, interrupt/resume, multitask strategies

Project Structure

src/langrove/
  cli.py              # Click CLI (serve, worker, init)
  settings.py          # Pydantic BaseSettings
  config.py            # langgraph.json parser
  app.py               # FastAPI app factory
  worker.py            # Background worker main loop
  exceptions.py        # Domain exceptions
  models/              # Pydantic DTOs
  db/                  # asyncpg repositories (raw SQL)
  services/            # Business logic (one service per domain)
  graph/               # Graph loading + registry
  streaming/           # SSE executor, formatter, broker
  queue/               # Redis Streams publisher, consumer, recovery
  auth/                # Auth handlers + middleware
  api/                 # FastAPI route handlers (thin, delegate to services)

.claude/
  settings.json        # Claude Code hooks + agent teams config
  agents/              # Subagent definitions (architect, implementer, reviewer)
  journals/            # Agent learnings across sessions

.github/
  workflows/           # CI + autonomous agent workflows
  ISSUE_TEMPLATE/      # Structured templates for features and bugs

Development

# Install dependencies
uv sync

# Run linter
uv run ruff check .

# Run formatter
uv run ruff format .

# Run tests
uv run pytest

# Run migrations
uv run langrove migrate

Autonomous Development (AI-Assisted)

Langrove uses a fully automated development lifecycle powered by Claude Code, inspired by the Helios Black Hole Architecture.

How it works

Planning (weekly)  →  Creates GitHub Issues from README vision + codebase gaps
       ↓
Execution (on label / every 6h)  →  Implements issue → branch → test → PR
       ↓
Review (on PR)  →  Automated code review + CI (lint + tests)
       ↓
Merge (on CI pass)  →  Auto-merge Claude's PRs
       ↓
Maintenance (weekly)  →  Deps, docs, dead code cleanup

GitHub Actions Workflows

Workflow Trigger What it does
ci.yml Push / PR Lint (ruff) + test (pytest)
claude.yml @claude mention Interactive AI agent in issues/PRs
claude-review.yml PR opened Automated code review
claude-implement.yml Issue labeled claude Autonomous implementation → PR
claude-backlog.yml Every 6 hours Picks up and implements backlog items
claude-planner.yml Weekly (Monday) Analyzes codebase, creates issues
claude-maintenance.yml Weekly (Sunday) Dependency and documentation updates
auto-merge.yml PR from claude/* branch Auto-merge after CI passes

Local Agent Teams

For complex features, spawn a team of specialized Claude Code agents:

Create an agent team to implement issue #42:
- Spawn an architect teammate to plan the approach
- Spawn 2 implementer teammates to build different modules
- Spawn a reviewer teammate to review their work
Require plan approval before implementation begins.

Subagent definitions in .claude/agents/ (architect, implementer, reviewer).

Agent Journals

Agents store learnings in .claude/journals/ (checked into git) so knowledge persists across CI runs. Each agent reads its journal before starting and appends new insights after completing work.

Principles

  • KISS, YAGNI, SOLID
  • Raw asyncpg SQL, no ORM
  • Services receive dependencies via constructor injection
  • API handlers are thin — delegate to services
  • No speculative abstractions — add when a second implementation appears

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

langrove-0.3.0.tar.gz (444.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

langrove-0.3.0-py3-none-any.whl (73.4 kB view details)

Uploaded Python 3

File details

Details for the file langrove-0.3.0.tar.gz.

File metadata

  • Download URL: langrove-0.3.0.tar.gz
  • Upload date:
  • Size: 444.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.2

File hashes

Hashes for langrove-0.3.0.tar.gz
Algorithm Hash digest
SHA256 236119ed24a52eb98eefa0f24b8bd0aee162a2ce40c2a3b85f6c41b01ecbcf22
MD5 4f6da6d5994ff9aac20ccf24c33b3c88
BLAKE2b-256 03985f4604f5f801058cb84a2c390d57dafc77c672e012275d5ad15e99cfdd9e

See more details on using hashes here.

File details

Details for the file langrove-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: langrove-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 73.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.2

File hashes

Hashes for langrove-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 955469f71be29c90f9143227257ea2f87d02b2a91fc5316d4567af78b7d51e1f
MD5 d9c7dff82ddf8f8895ed8cc33d1414b8
BLAKE2b-256 9c1db03b4d9e5216787c3601ec4ea662a3a8ddee1124e7c278429477862356d1

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page