Documentation

LangStitch docs

Visual LangGraph IDE — canvas, asset designers, Python 3.13 export, and platform deployment. Install via downloadable VSX or LangTailor desktop.

Quick start

Install the canvas extension

Download the .vsix from GitHub Releases or install from Open VSX. Open any *.langstitch.json file to design graphs.

Download VSX →

Platform API (Docker)

docker run -p 8787:8787 \
  ghcr.io/langstitch/langstitch-api:latest

Platform API at http://localhost:8787

SDK (PyPI)

pip install langstitch-sdk
langstitch new my-agent

Build LangGraph apps with the Python SDK

SDK user guide

A complete, production-oriented guide to the langstitch Python SDK. The three core steps — Requirements, First Project, Specification — get you running; the Production sections cover configuration, secrets, observability, resilience, security, testing, deployment, and CI you need before shipping.

1 · Requirements

Extras matrix

ExtraPulls inEnables
(core)PyYAMLdecorators, config, registries, context, CLI scaffold
serverfastapi, uvicornlangstitch run, create_app() (/health, /info, /invoke)
graphlanggraph, langchain-coreGraphBuilder.compile() → real StateGraph
llmlangchainget_llm_provider() (+ a provider pkg, e.g. langchain-openai)
httphttpxget_http_client() / external services
allall of the aboveeverything
# pick what you need
pip install langstitch-sdk                               # core only
pip install "langstitch-sdk[server,graph,llm,http]"      # typical agent service
pip install "langstitch-sdk[all]"                        # everything

Design note: importing langstitch never imports FastAPI, LangGraph, LangChain, or httpx. Each helper imports its dependency lazily and raises a clear, actionable error if the extra is missing — so the core stays light and safe to import in tooling/CI.

2 · First project

langstitch new my-agent      # scaffold
cd my-agent
python -m venv .venv && . .venv/bin/activate   # (Windows: .venv\Scripts\activate)
pip install -e ".[server,graph,llm,http]"      # install with extras
python -m app                # bootstrap + print registered components (JSON)
pytest -q                    # run the generated smoke tests
langstitch run               # start the API server

Generated layout — every concept gets its own package:

my-agent/
  application.yaml     # application config (or precompiled application.json)
  env.yaml             # runtime environment variables (gitignored in real projects)
  pyproject.toml       # depends on langstitch; your package is "app"
  app/
    __init__.py        # imports submodules so decorators register on import
    graphs/            # @graph — main graph (main.py) + subgraphs
    nodes/             # @graph_node — node handlers (state -> dict)
    skills/            # @skill
    guardrails/        # @input_guardrail / @output_guardrail
    policies/          # @business_policy
    personas/          # @persona
    tools/             # @tool
    agents/            # @worker_agent
    mcp/               # @langstitch_mcp_server + @mcp_tool/resource/prompt
    config.py          # @configuration — typed application.yaml sections
    state.py           # graph state schema (TypedDict)
    main.py            # @langstitch_graph_server — server + bootstrap()
  tests/               # smoke tests

How registration works: decorators record a spec on a process-global registry at import time. app/__init__.py imports every submodule, so a single import app wires up the whole application. Keep decorated modules import-safe (no network/IO at module scope) so import stays fast and deterministic.

CLI reference

CommandWhat it does
langstitch new <name> [--dir P] [--force]scaffold a project
langstitch info [--root P]load config & list all registered components
langstitch run [--root P] [--host] [--port]start the API server
langstitch compile [--root P]application.yaml → application.json (fast startup)
langstitch get <json.path> [--root P]resolve a config path, print as JSON
langstitch versionprint the SDK version

3 · Specification

Describe the agent declaratively: configuration in application.yaml (or application.json), components via decorators.

application.yaml

app:
  name: my_agent
  version: 0.1.0
  description: Support agent built with LangStitch.

model:
  provider: openai          # passed to LangChain init_chat_model
  name: gpt-4o-mini
  temperature: 0.2

server:
  host: 0.0.0.0
  port: 8000

http:
  timeout: 30               # default for get_http_client() (no service)

external_services:          # see §5
  billing:
    serverUrl: https://api.billing.com
    basePath: /v1
    propagate_headers: [x-request-id, authorization]
    auth:
      type: bearer
      token: ${BILLING_TOKEN}

Components (decorators)

from langstitch import (
    graph, graph_node, skill, persona,
    input_guardrail, output_guardrail, business_policy,
    tool, worker_agent, configuration,
    langstitch_graph_server, GraphBuilder, END,
)

@persona(role="assistant", tone="helpful, concise")
def assistant() -> str:
    return "You are a helpful LangStitch support assistant."

@tool(tags=["billing"], roles=["agent"])
def lookup_invoice(invoice_id: str) -> dict:
    """Fetch an invoice by id."""
    ...

@input_guardrail(description="Reject empty/oversized input.", action="block")
def non_empty(text: str) -> bool:
    return bool(text and 0 < len(text) <= 8000)

@business_policy(priority=100, description="Deny refunds over policy limit.")
def refund_limit(context: dict) -> dict:
    amount = context.get("amount", 0)
    return {"decision": "deny" if amount > 1000 else "allow"}

@graph_node(description="Answer the latest message.")
def respond(state: dict) -> dict:
    ...
    return {"response": "...", "messages": [...]}

@graph(name="main", entrypoint=True)
def main_graph() -> GraphBuilder:
    g = GraphBuilder("main")
    g.add_node("respond", respond)
    g.set_entry_point("respond")
    g.add_edge("respond", END)
    return g

@langstitch_graph_server(name="my-agent", protocol="http", port=8000)
class Server:
    """Graph API server."""

4 · Configuration & secrets

Two files drive an app, both resolved from the project root. load_config() runs once at bootstrap and becomes the active in-memory store.

Reading config (JSON-path)

from langstitch import get_config

get_config()                              # whole AppConfig (cached)
get_config("server.port")                 # -> 8000  (scalar)
get_config("model")                       # -> {...} (nested object)
get_config("external_services.billing.auth.type")
get_config("items[0].name")               # array index, negative allowed
get_config("missing.key", default="x")    # safe default
get_config("server", as_json=True)        # -> '{"host": ...}'

Typed config with @configuration

from dataclasses import dataclass
from langstitch import configuration

@configuration(section="server")
@dataclass
class ServerConfig:
    host: str = "0.0.0.0"
    port: int = 8000

# after load_config(): values are bound + type-coerced
cfg = ServerConfig._langstitch_instance   # ServerConfig(host="0.0.0.0", port=8000)
Secrets discipline. Never commit real secrets. Keep env.yaml out of version control (the scaffold gitignores it) and inject secrets through your platform/orchestrator in production. Use ${ENV_VAR} interpolation in application.yaml so the file references secrets without containing them.

5 · Models & HTTP services

LLM provider

from langstitch import get_llm_provider

# reads model.provider / model.name / model.temperature from config;
# API key comes from the environment (populated by env.yaml)
llm = get_llm_provider()                       # or get_llm_provider("gpt-4o", temperature=0)
result = llm.invoke([{"role": "user", "content": "hi"}])

External services with auth + path/query params

from langstitch import get_http_client, set_request_headers

# In server middleware, record inbound headers once per request:
set_request_headers(request.headers)            # enables header propagation

api = get_http_client("billing")                # base_url, timeout, auth, propagated headers wired in
resp = api.get("/invoices/{id}", path_params={"id": 42},
               params={"expand": "lines"}, headers={"X-Trace": "1"})
api.post("/invoices", json={"amount": 10})
api.put("/invoices/{id}", path_params={"id": 42}, json={...})
api.patch("/invoices/{id}", path_params={"id": 42}, json={...})
api.delete("/invoices/{id}", path_params={"id": 42})
# also: head/options, api.request("GET", ...), set_header/add_headers/remove_header
# async: get_async_http_client("billing") -> await api.get(...); raw=True for bare httpx

Supported auth.type values:

typeoptionseffect
noneno credentials
basicusername, passwordAuthorization: Basic <b64>
bearertokenAuthorization: Bearer <token>
api_keyname (def. X-API-Key), value, in (header|query)header or query param
oauth2token_url, client_id, client_secret, scope?, audience?client-credentials; token cached + refreshed

6 · Context & registries

Hierarchical context keeps parent state clean: every LLM/sub-agent call runs in an isolated child scope and only its final output merges back. Tools are lazily materialized — a node pulls only the subset it needs.

from langstitch import Context, run_llm, run_worker_agent

ctx = Context(data={"question": q}, messages=[{"role": "user", "content": q}])

def call_model(llm_ctx):
    # llm_ctx.system = resolved persona; llm_ctx.tools = materialized just for this call
    return get_llm_provider().invoke(
        [{"role": "system", "content": llm_ctx.system}] + llm_ctx.messages,
    )

answer = run_llm(ctx, call_model, persona="assistant",
                 tool_tags=["billing"], key="answer", as_message="assistant")
# ctx.data["answer"] set; the child's tool traffic + scratch were discarded.

# Delegate to a sub-agent (runs with only its allowed tools, isolated context):
findings = run_worker_agent(ctx, "researcher", carry=["question"])

Dynamic registries are refreshable and lazy — they index decorator specs plus optional external loaders, auto-detect changes, and never eagerly instantiate everything. The graph server exposes them for introspection:

Server.get_all_tools(); Server.get_all_worker_agents()
Server.get_input_guardrails(); Server.get_output_guardrails()
Server.get_skills(); Server.get_policies(); Server.get_personas()
Server.get_tool("lookup_invoice"); Server.refresh_registries()
# module-level equivalents: langstitch.get_all_tools(), ... , refresh_registries()

7 · Production hardening

Observability & logging

from langstitch import get_logger
log = get_logger(__name__)        # level from LOG_LEVEL env (default INFO)
log.info("handled request", extra={"intent": intent})

Guardrails, policies & error handling

Security checklist

Testing

from langstitch import get_registry, reset_registry, Context, run_worker_agent

def test_components_register():
    import app                       # triggers registration
    reg = get_registry()
    assert "respond" in reg.nodes and reg.server is not None

def test_context_isolation():
    parent = Context(data={"topic": "x"})
    out = run_worker_agent(parent, "researcher", carry=["topic"])
    assert "researcher" in parent.data and "messages" not in parent.data  # no leakage

Use reset_registry() / reset_config_cache() between tests for isolation. Mock external HTTP with httpx MockTransport; the SDK's own suite runs on Python 3.10–3.13 in CI.

8 · Deployment & CI

Container image

FROM python:3.12-slim
WORKDIR /app
COPY pyproject.toml ./
RUN pip install --no-cache-dir ".[server,graph,llm,http]"
COPY . .
RUN langstitch compile           # precompile application.json for fast startup
EXPOSE 8000
CMD ["langstitch", "run", "--host", "0.0.0.0", "--port", "8000"]

Provide secrets at runtime (-e OPENAI_API_KEY=... / orchestrator secrets), not in the image. The server exposes /health (liveness), /info (registered components), and /invoke.

CI & release

9 · API reference

SymbolKindPurpose
@graph_nodedecoratornode handler state → dict
@graphdecoratorgraph builder (entrypoint=, parent=)
@skilldecoratorreusable capability
@input_guardrail / @output_guardraildecoratorinbound/outbound validation
@business_policydecoratorpriority-ordered org rule
@personadecoratoridentity / system prompt
@configurationdecoratorbind a config section to a dataclass
@tool / @worker_agentdecoratorLLM tool / delegatable sub-agent
@langstitch_graph_serverdecoratorgraph API server (protocol, port, name, properties)
@langstitch_mcp_server + @mcp_tool/resource/promptdecoratorMCP server & primitives
GraphBuilder, START, ENDclass/constdeclarative wiring → LangGraph
LangStitchAppclassruntime: bootstrap(), build_graph(), invoke(), info()
load_config, get_config, compile_config, query_jsonfnconfig load + JSON-path access
get_llm_provider, get_http_client, get_async_http_clientfnconfigured clients
get_logger, get_env, get_secretfnlogging & env access
set_request_headers, load_service_config, ServiceClientfn/classexternal services + header propagation
Context, ContextScope, ContextBuilder, run_llm, run_worker_agentclass/fnhierarchical context
get_all_tools, get_all_worker_agents, refresh_registries, …fndynamic registries

Exhaustive signatures and options live in the SDK docs and the langstitch package on PyPI.

10 · Troubleshooting

SymptomCause & fix
RuntimeError: … requires <pkg>missing extra — pip install "langstitch-sdk[server|graph|llm|http]"
No graph registeredmark the root graph @graph(entrypoint=True) and ensure its module is imported
Component missing from infoits module isn't imported — add it to app/__init__.py
external service … not configuredadd it under external_services in config (or pass the right name)
Secret is empty downstreamset the env var; check ${VAR} spelling and env.yaml flattening
Config edits not picked upa precompiled application.json wins — re-run langstitch compile

Canvas & nodes

LLM

Model, prompts, temperature, bound tools/agents.

Tool

Inline, registry, or MCP Studio tools.

Agent

Sub-agent, remote, A2A, or registry delegation.

RAG Agent

Retrieval-augmented node bound to a RAG pipeline.

Multi-Intent Classifier

Special decision node — route by classified intents.

Decision / Function / Subgraph

Conditional edges, Python transforms, nested graphs.

Asset designers

Open the sidebar Assets tab to configure reusable project artifacts. All export to dedicated Python modules.

SDK Component Designer (custom nodes, connectors & adaptors)

The Components designer tab lets you build reusable custom components without editing source. A component is a declarative manifest — identity, ports, a typed config-field schema, a theme, and a safe Python codegen template — that renders on the canvas alongside the built-in nodes and survives the project round-trip.

Author a component

Safe codegen template

Templates use whitelisted placeholder substitution — there is no eval or arbitrary code execution in the IDE. Values are escaped per field kind, and secret fields are emitted as os.environ.get(...) rather than inlined.

# Template (authored in the designer)
def {{nodeName}}(state: State) -> dict:
    """{{label}} — {{description}}"""
    import httpx
    url = {{field.url}}                 # string  → safe Python literal
    timeout = {{field.timeout}}         # number
    api_key = {{field.api_key}}         # secret  → os.environ.get("...")
    resp = httpx.request({{field.method}}, url, timeout=timeout)
    return {"{{outputKey}}": resp.text}
PlaceholderResolves to
{{nodeName}}generated function name for the placed node
{{label}} / {{description}}node label / description (escaped for docstrings)
{{outputKey}}the node's output state key
{{field.<id>}}a config value, escaped by kind (secret → os.environ.get)
{{field.<id>.raw}}verbatim injection — allowed for code fields only

Place, configure, export & share

Additive & safe. Custom components are stored in componentRegistry on the project document (GraphDocument v1.2); older .langstitch.json projects load unchanged. The built-in node kinds are untouched.

Python 3.13 export

Platform → Export → Python produces a buildable multi-module ZIP:

src/<package>/
  graphs/        # StateGraph definitions
  nodes/         # Per-node handlers
  skills/        # Skill modules
  prompts/       # Prompt templates
  tools/         # Tool registry
  resources/     # Static resources
  connections/   # MCP, remote, A2A
  guardrails/    # Guardrail policies
  rules/         # Business rules
  personas/      # Agent personas
  rag/           # RAG pipelines
langsmith.json   # LangSmith + LangStitch IDE metadata (incl. registries.components)
langstitch.project.json   # full project incl. componentRegistry (custom components)
pip install -e ".[dev]"
pytest
python -m <package>

Platform panel

Workspaces: ~/.langstitch/workspaces/<project-id>/

Integrations

GitHub Actions

Testing

npx playwright install chromium
npm run test:e2e
npm run agent:run