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 compatibility — get_client() works out of the box
  • SSE streaming — useStream() 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

Metadata

Release files for langrove 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for langrove 0.3.0
File Size Uploaded Provenance
langrove-0.3.0.tar.gz 444.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for langrove 0.3.0
File Interpreter ABI Platform
langrove-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 518.3 kB

Release files / langrove-0.3.0.tar.gz

Download URL langrove-0.3.0.tar.gz
Size 444.9 kB
Tags Source
SHA-256 checksum
How to use checksums
236119ed24a52eb98eefa0f24b8bd0aee162a2ce40c2a3b85f6c41b01ecbcf22
BLAKE2b-256 checksum
How to use checksums
03985f4604f5f801058cb84a2c390d57dafc77c672e012275d5ad15e99cfdd9e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.7.2

Release files / langrove-0.3.0-py3-none-any.whl

Download URL langrove-0.3.0-py3-none-any.whl
Size 73.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
955469f71be29c90f9143227257ea2f87d02b2a91fc5316d4567af78b7d51e1f
BLAKE2b-256 checksum
How to use checksums
9c1db03b4d9e5216787c3601ec4ea662a3a8ddee1124e7c278429477862356d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.7.2

Security for langrove 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page