Skip to main content

Planar

Planar is a batteries-included Python framework for building durable workflows, agent automations, and stateful APIs. Built on FastAPI and SQLModel, it combines orchestration, data modeling, and file management into a cohesive developer experience.

Feature Highlights

  • Durable workflow engine with resumable async steps, automatic retries, and suspension points
  • Agent step framework with first-class support for OpenAI, Anthropic, and other providers
  • Human task assignments and rule engine tooling baked into workflow execution
  • SQLModel-powered data layer with Alembic migrations and CRUD scaffolding out of the box
  • Built-in file management and storage adapters for local disk, Amazon S3, and Azure Blob Storage
  • CLI-driven developer workflow with templated scaffolding, hot reload, and environment-aware configuration

Installation

Planar is published on PyPI. Add it to an existing project with uv:

uv add planar

To explore the CLI without updating pyproject.toml, use the ephemeral uvx runner:

uvx planar --help

Quickstart

Generate a new service, start up the dev server, and inspect the auto-generated APIs:

uvx planar scaffold --name my_service
cd my_service
uv run planar dev src/main.py

Open http://127.0.0.1:8000/docs to explore your service's routes and workflow endpoints. The scaffold prints the exact app path if it differs from src/main.py.

Define a Durable Workflow

from datetime import timedelta

from planar import PlanarApp
from planar.workflows import step, suspend, workflow


@step
async def charge_customer(order_id: str) -> None: ...


@step
async def notify_success(order_id: str) -> None: ...


@workflow
async def process_order(order_id: str) -> None:
    await charge_customer(order_id)
    await suspend(interval=timedelta(hours=1))
    await notify_success(order_id)


app = PlanarApp()
app.register_workflow(process_order)

Workflows are async functions composed of resumable steps. Planar persists every step, applies configurable retry policies, and resumes suspended workflows even after process restarts. Check docs/workflows.md for deeper concepts including event-driven waits, human steps, and agent integrations.

Core Capabilities

  • Workflow orchestration: Compose async steps with guaranteed persistence, scheduling, and concurrency control.
  • Agent steps: Run LLM-powered actions durably with provider-agnostic adapters and structured prompts.
  • Human tasks and rules: Build human-in-the-loop approvals and declarative rule evaluations alongside automated logic.
  • Stateful data and files: Model entities with SQLModel, manage migrations through Alembic, and store files using pluggable backends.
  • Observability: Structured logging and OpenTelemetry hooks surface workflow progress and performance metrics.

Command Line Interface

uvx planar scaffold --help   # generate a new project from the official template
uv run planar dev [PATH]     # run with hot reload and development defaults
uv run planar prod [PATH]    # run with production defaults
uv run planar install-skill  # install Planar guidance for coding agents

[PATH] points to the module that exports a PlanarApp instance (defaults to app.py or main.py). Use --config PATH to load a specific configuration file and --app NAME if your application variable is not named app.

planar scaffold and its planar init alias install the coding-agent skill by default. In an existing project, run uv run planar install-skill from the project root. The command links .agents/skills/planar-app-development and .claude/skills/planar-app-development to the skill bundled with the installed Planar package, so the guidance changes with the package version instead of becoming a stale generated copy. Use --no-install-skill when scaffolding if the project should not expose the skill.

Configuration

Planar merges environment defaults with an optional YAML override. By convention it looks for planar.dev.yaml, planar.prod.yaml, or planar.yaml in your project directory, but you can supply a path explicitly via --config or the PLANAR_CONFIG environment variable.

Example minimal override:

ai_models:
  default: invoice_llm
  providers:
    public_openai:
      factory: openai_responses
      options:
        api_key: ${OPENAI_API_KEY}
        base_url: ${OPENAI_PROXY_URL}
    azure_llm:
      factory: azure_openai_responses
      options:
        endpoint: ${AZURE_OPENAI_ENDPOINT}
        # optional: api_key: ${AZURE_OPENAI_KEY} (omit to use DefaultAzureCredential)
  models:
    invoice_llm:
      provider: public_openai
      options: gpt-4o-mini
    claims_llm:
      provider: azure_llm
      options:
        deployment: gpt-4o-claims

storage:
  directory: .files

Set default to the model key agents should use when they leave model=None. Use ConfiguredModelKey("invoice_llm") to reference a specific entry. Providers let you reuse auth/transport settings across multiple models; factories receive merged provider + model options.

ai_models:
  providers:
    public_openai:
      factory: openai_responses
      options:
        api_key_env: BILLING_OPENAI_KEY
  models:
    invoice_parsing_model:
      provider: public_openai
      options: gpt-4o-mini

For Azure OpenAI endpoints:

ai_models:
  providers:
    azure_llm:
      factory: azure_openai_responses
      options:
        endpoint_env: AZURE_OPENAI_ENDPOINT
        deployment_env: AZURE_OPENAI_DEPLOYMENT
        # Optional: omit these to use DefaultAzureCredential instead
        api_key: ${AZURE_OPENAI_KEY}
  models:
    invoice_parsing_model:
      provider: azure_llm
      options:
        deployment: gpt-4o-mini

Omit the API key options to authenticate with DefaultAzureCredential (managed identity or user credentials). Use token_scope/token_scope_env if you need to override the default https://cognitiveservices.azure.com/.default scope.

Register custom factories on the app and reference them by key in config:

from planar import PlanarApp

app = PlanarApp(...)
app.register_model_factory("vertex_gemini", vertex_gemini_model_factory)

Factories receive the raw options dict plus a PlanarConfig reference, so you can inject per-environment parameters without touching workflow code. They can be synchronous callables or async coroutines—Planar automatically handles awaiting them and caching the resulting model instances.

Set provider credentials through environment variables (e.g., OPENAI_API_KEY for the OpenAI entries above). For more configuration patterns and workflow design guidance, browse the documents in docs/.

Security & User Attribution

Planar records which user started or cancelled a workflow run when JWT authentication is enabled via security.jwt in your config. The authenticated user's email (from the user_email JWT claim) is stored on the workflow run. The Coplane frontend surfaces this in the runs table, run popover, and run detail page.

Examples

  • examples/expense_approval_workflow — human approvals with AI agent collaboration
  • examples/event_based_workflow — event-driven orchestration and external wakeups
  • examples/simple_service — CRUD service paired with workflows

Run any example with uv run planar dev path/to/main.py.

Testing

For testing your workflows, you can use the planar.testing module. This module provides a PlanarTestClient class that can be used to test your Planar application.

Planar also publishes a pytest plugin with namespaced fixtures such as planar_app, planar_client, planar_session, and planar_observer. Pytest loads installed plugins automatically by default. If your test suite sets PYTEST_DISABLE_PLUGIN_AUTOLOAD or runs pytest with --disable-plugin-autoload, enable Planar explicitly in your conftest.py:

pytest_plugins = ["planar.testing.pytest"]

For more information, see docs/testing_workflows.md.

Local Development

Planar is built with uv. Clone the repository and install dev dependencies:

uv sync --extra otel

Useful commands:

  • uv run ruff check --fix and uv run ruff format to lint and format
  • uv run pyright for static type checking
  • uv run pytest to run the test suite (use -n auto for parallel execution)
  • uv run pytest --cov=planar to collect coverage
  • uv tool install pre-commit && uv tool run pre-commit install to enable git hooks

PostgreSQL Test Suite

By default, PostgreSQL-backed tests start a temporary Docker container and create a fresh test_<uuid> database for each test run, then drop that database during teardown.

docker run --restart=always --name planar-postgres \
  -e POSTGRES_PASSWORD=postgres \
  -p 127.0.0.1:5432:5432 \
  -d docker.io/library/postgres

PLANAR_TEST_POSTGRESQL=1 PLANAR_TEST_POSTGRESQL_CONTAINER=planar-postgres \
  uv run pytest -s

Disable SQLite with PLANAR_TEST_SQLITE=0.

If you already have a local PostgreSQL server running, you can bypass Docker by pointing the tests at a server-level connection string. The fixture will still create and clean up an isolated test_<uuid> database for each test run.

PLANAR_TEST_SQLITE=0 \
PLANAR_TEST_POSTGRESQL=1 \
PLANAR_TEST_POSTGRESQL_SERVER_URL=postgresql+asyncpg://postgres@127.0.0.1:5432 \
uv run pytest -s

Cairo SVG Dependencies

Some AI integration tests convert SVG assets using cairosvg. Install Cairo libraries locally before running those tests:

brew install cairo libffi pkg-config
export DYLD_FALLBACK_LIBRARY_PATH="/opt/homebrew/lib:${DYLD_FALLBACK_LIBRARY_PATH}"

Most Linux distributions ship the required libraries via their package manager.

Documentation

Use uv run planar docs to view the documentation in your terminal - this is particularly useful to equip coding agents with context about Planar. Alternatively, use docs/llm_prompt.md as a drop-in reference document in whatever tool you are using.

Dive deeper into Planar's design and APIs in the docs/ directory:

  • docs/workflows.md
  • docs/agents.md
  • docs/system_api_invariants.md
  • docs/design/agent_extensions.md
  • docs/design/event_based_waiting.md
  • docs/design/human_step.md

Release files for planar 0.38.0

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

Built distribution (wheel)

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

Release files / planar-0.38.0-py3-none-any.whl

Download URL planar-0.38.0-py3-none-any.whl
Size 567.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8a1cf48f0e740c7b3b272f47ddc6750bcb6e1dc042c8dbf4f6695c2c370dafbd
BLAKE2b-256 checksum
How to use checksums
c867307a38f27b1b3a07a34eb32a0bc84f557f611d07a0cfaef1031460d1b889
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 20, 2026.

Transparency log

Release history Release notifications | RSS feed

0.40.1

1 release file

0.40.0

1 release file

0.39.0

1 release file

0.38.1

1 release file

This release

0.38.0 This release

1 release file

0.37.0

1 release file

0.36.1

1 release file

0.36.0

1 release file

0.35.0

1 release file

0.34.1

1 release file

0.34.0

1 release file

0.33.2

1 release file

0.33.1

1 release file

0.33.0

1 release file

0.32.4

1 release file

0.32.3

1 release file

0.32.2

1 release file

0.32.1

1 release file

0.32.0

1 release file

0.31.9

1 release file

0.31.8

1 release file

0.31.7

1 release file

0.31.6

1 release file

0.31.5

1 release file

0.31.4

1 release file

0.31.3

1 release file

0.31.2

1 release file

0.31.1

1 release file

0.31.0

1 release file

0.30.4

1 release file

0.30.3

1 release file

0.30.2

1 release file

0.30.1

1 release file

0.30.0

1 release file

0.29.2

1 release file

0.29.1

1 release file

0.29.0

1 release file

0.28.0

1 release file

0.27.0

1 release file

0.26.0

1 release file

0.25.0

1 release file

0.24.0

1 release file

0.23.2

1 release file

0.23.1

1 release file

0.23.0

1 release file

0.22.2

1 release file

0.22.1

1 release file

0.22.0

1 release file

0.21.1

1 release file

0.21.0

1 release file

0.20.2

1 release file

0.20.1

1 release file

0.20.0

1 release file

0.19.1

1 release file

0.19.0

1 release file

0.18.0

1 release file

0.17.0

1 release file

0.16.0

1 release file

0.15.0

1 release file

0.14.0

1 release file

0.13.9

1 release file

0.13.8

1 release file

0.13.7

1 release file

0.13.6

1 release file

0.13.5

1 release file

0.13.4

1 release file

0.13.3

1 release file

0.13.2

1 release file

0.13.0

1 release file

0.12.0

1 release file

0.11.0

1 release file

0.10.0

1 release file

0.9.3

1 release file

0.9.2

1 release file

0.9.1

1 release file

0.9.0

1 release file

0.8.0

1 release file

0.7.0

1 release file

0.5.0

1 release file

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