Skip to main content

Composable middleware framework for LangGraph agents

Project description

langchain-agentkit

Composable middleware framework for LangGraph agents.

Python License: MIT

Build LangGraph agents with reusable middleware that composes tools and prompts. Two layers to choose from:

  • agent metaclass — declare a class, get a complete ReAct agent with middleware-composed tools and prompts
  • AgentKit — primitive composition engine for full control over graph topology

Installation

Requires Python 3.11+, langchain-core>=0.3, langgraph>=0.4.

pip install langchain-agentkit

Quick Start

The agent metaclass (recommended)

Declare a class that inherits from agent to get a StateGraph with an automatic ReAct loop:

from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
from langchain_agentkit import agent, SkillsMiddleware, TasksMiddleware

class researcher(agent):
    llm = ChatOpenAI(model="gpt-4o")
    tools = [web_search]
    middleware = [SkillsMiddleware("skills/"), TasksMiddleware()]
    prompt = "You are a research assistant."

    async def handler(state, *, llm, prompt):
        messages = [SystemMessage(content=prompt)] + state["messages"]
        response = await llm.ainvoke(messages)
        return {"messages": [response], "sender": "researcher"}

# Compile and use
graph = researcher.compile()
result = graph.invoke({"messages": [HumanMessage("Size the B2B SaaS market")]})

# With checkpointer for interrupt() support
from langgraph.checkpoint.memory import InMemorySaver
graph = researcher.compile(checkpointer=InMemorySaver())

AgentKit for manual graph wiring

Use AgentKit when you need full control over graph topology — multi-node graphs, shared ToolNode, custom routing:

from langchain_agentkit import AgentKit, SkillsMiddleware, TasksMiddleware

kit = AgentKit([
    SkillsMiddleware("skills/"),
    TasksMiddleware(),
])

# In any graph node:
all_tools = my_tools + kit.tools
system_prompt = kit.prompt(state, runtime)

Standalone SkillRegistry

Use SkillRegistry directly for skill discovery without the middleware layer:

from langchain_agentkit import SkillRegistry

registry = SkillRegistry("skills/")
tools = registry.tools  # [Skill, SkillRead]

Examples

See examples/ for complete working code:

API Reference

agent

Declarative agent builder. Subclassing produces a StateGraph. Call .compile() to get a runnable graph.

Class attributes:

Attribute Required Description
llm Yes Language model instance
tools No List of LangChain tools
middleware No Ordered list of Middleware instances
prompt No System prompt — inline string, file path, or list of either

Handler signature:

async def handler(state, *, llm, tools, prompt, runtime): ...

state is positional. Everything after * is keyword-only and injected by name — declare only what you need:

Parameter Type Description
state dict LangGraph state (positional, required)
llm BaseChatModel LLM pre-bound with all tools via bind_tools()
tools list[BaseTool] All tools (user tools + middleware tools)
prompt str Fully composed prompt (template + middleware sections)
runtime ToolRuntime Unified runtime context. Use runtime.config for the full RunnableConfig

Both sync and async handlers are supported — sync handlers are detected via inspect.isawaitable and awaited automatically.

Custom state types — annotate the handler's state parameter:

class MyState(TypedDict, total=False):
    messages: Annotated[list, add_messages]
    draft: dict | None

class my_agent(agent):
    llm = ChatOpenAI(model="gpt-4o")

    async def handler(state: MyState, *, llm):
        ...

Without an annotation, AgentState is used by default.

Middleware protocol

Any class with tools (property) and prompt(state, runtime) (method) satisfies the protocol via structural subtyping — no base class needed:

class MyMiddleware:
    @property
    def tools(self) -> list[BaseTool]:
        return [my_tool]

    def prompt(self, state: dict, runtime: ToolRuntime) -> str | None:
        return "You have access to my_tool."

Built-in middleware:

Middleware Tools Prompt
SkillsMiddleware(skills_dirs) Skill, SkillRead Progressive disclosure skill list with load instructions
TasksMiddleware() TaskCreate, TaskUpdate, TaskList, TaskGet Base agent behavior + task context with status icons

TasksMiddleware

Task management middleware with Command-based tools that update graph state via LangGraph's ToolNode.

# Default — auto-creates task tools
mw = TasksMiddleware()

# Custom tools
mw = TasksMiddleware(task_tools=[my_create, my_update])

# Custom task formatter
mw = TasksMiddleware(formatter=my_format_function)

Task tools use InjectedState to read tasks from state and return Command(update={"tasks": [...]}) to apply changes. Task state is updated locally within the agent's graph, visible to the prompt on every ReAct loop iteration. When used as a subgraph, state flows back to the parent graph on completion.

You can also create task tools directly:

from langchain_agentkit import create_task_tools

tools = create_task_tools()  # [TaskCreate, TaskUpdate, TaskList, TaskGet]

AgentKit(middleware, prompt=None)

Composition engine that merges tools and prompts from middleware.

  • tools — All tools from all middleware, deduplicated by name (first middleware wins, cached)
  • prompt(state, runtime) — Template + middleware sections, joined with double newline (dynamic per call)

Prompt templates can be inline strings, file paths, or a list of either:

kit = AgentKit(middleware, prompt="You are helpful.")
kit = AgentKit(middleware, prompt=Path("prompts/system.txt"))
kit = AgentKit(middleware, prompt=["prompts/base.txt", "Extra instructions"])

node

Skill-aware metaclass. Uses skills attribute instead of middleware. Consider using agent for the full middleware composition model.

class my_agent(node):
    llm = ChatOpenAI(model="gpt-4o")
    tools = [web_search]
    skills = "skills/"  # str, list[str], or SkillRegistry instance

    async def handler(state, *, llm, tools, runtime): ...

AgentState

Minimal LangGraph state type with task support:

Field Type Description
messages Annotated[list, add_messages] Conversation history with LangGraph message reducer
sender str Name of the last node that produced output
tasks list[dict[str, Any]] Task list managed by TasksMiddleware tools

Extend with your own fields:

class MyState(AgentState):
    current_project: str
    iteration_count: int

Security

  • Path traversal prevention: Skill file paths resolved to absolute and checked against skill directories. Reference file names reject . and .. patterns.
  • Name validation: Skill names validated per AgentSkills.io spec — lowercase alphanumeric + hyphens, 1-64 chars.
  • Tool scoping: Each agent only has access to the tools declared in its tools attribute plus middleware-provided tools.
  • Prompt trust boundary: Prompt templates and middleware prompt sections are set by the developer at construction time, not by end-user input.

Contributing

git clone https://github.com/rsmdt/langchain-agentkit.git
cd langchain-agentkit
uv sync --extra dev
uv run pytest --tb=short -q
uv run ruff check src/ tests/
uv run mypy src/

Project details


Download files

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

Source Distribution

langchain_agentkit-0.5.0.tar.gz (126.9 kB view details)

Uploaded Source

Built Distribution

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

langchain_agentkit-0.5.0-py3-none-any.whl (32.8 kB view details)

Uploaded Python 3

File details

Details for the file langchain_agentkit-0.5.0.tar.gz.

File metadata

  • Download URL: langchain_agentkit-0.5.0.tar.gz
  • Upload date:
  • Size: 126.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for langchain_agentkit-0.5.0.tar.gz
Algorithm Hash digest
SHA256 47e1d0286bb58f0fd9f5cad133c24678054b55ba5cfc965dff3736d16b950966
MD5 a2ac64172d775f444b72b80be90e344d
BLAKE2b-256 d8c39418da935acf2dd5007b17cbae16bd278bac9dd7aea67dca8cb19312a2da

See more details on using hashes here.

File details

Details for the file langchain_agentkit-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: langchain_agentkit-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 32.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for langchain_agentkit-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d965f76c6ef38cf58c74a35e85b03059c9001d54b2fbc27622a1ae438d0ca927
MD5 2dcc0981c38fccab563ebe4f91e27fc4
BLAKE2b-256 ca253e2b52cf81c32fe1b9244b06c12214ccf73823ea84b966d338a116152906

See more details on using hashes here.

Supported by

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