langchain-agent-server
Lightweight server for LangChain and LangGraph agents. Serve any agent as a REST API with invoke and streaming endpoints.
No vendor lock-in. No paid platform. Just FastAPI + SSE.
Install
pip install langchain-agent-server
Quick Start
from langchain_agent_server import create_app, NoAuth
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
# Build your agent
llm = ChatOpenAI(model="gpt-4o")
agent = create_react_agent(llm, tools=[...])
# Wrap it in a server
async def agent_factory(auth_context):
return agent
app = create_app(
title="My Agent API",
agent_factory=agent_factory,
auth=NoAuth(),
)
Run with any ASGI server:
uvicorn myapp:app --host 0.0.0.0 --port 8000
Or directly:
if __name__ == "__main__":
host = os.getenv("HOST", "0.0.0.0")
port = int(os.getenv("PORT", "8124"))
uvicorn.run(app, host=host, port=port, log_level="info")
Endpoints
GET /health
Health check. Returns {"status": "ok"}.
POST /invoke
Invoke the agent and get all response messages at once.
Request:
{
"input": {
"messages": [
{"role": "user", "content": "What's the weather in NYC?"}
]
}
}
Response:
{
"result": {
"messages": [
{"type": "human", "role": "user", "content": "What's the weather in NYC?"},
{"type": "ai", "role": "assistant", "content": "", "tool_calls": [
{"name": "get_weather", "args": {"city": "NYC"}}
]},
{"type": "tool", "role": "tool", "name": "get_weather", "content": "72°F, sunny"},
{"type": "ai", "role": "assistant", "content": "It's 72°F and sunny in NYC!"}
]
}
}
POST /stream
Invoke the agent and stream events via Server-Sent Events (SSE).
Same request format as /invoke. Returns text/event-stream.
Events:
data: {"type": "token", "content": "It's"}
data: {"type": "token", "content": " 72"}
data: {"type": "tool_start", "name": "get_weather", "input": {"city": "NYC"}}
data: {"type": "tool_end", "name": "get_weather", "output": "72°F, sunny"}
data: {"type": "token", "content": "It's 72°F and sunny!"}
data: {"type": "end", "messages": [...]}
Event types:
| Type | Description |
|---|---|
token |
A text chunk streamed from the LLM |
tool_start |
Agent is calling a tool (includes name and input) |
tool_end |
Tool returned a result (includes name and output) |
error |
An error occurred (includes error and error_type) |
end |
Stream complete (includes final messages array) |
Authentication
Bearer Token (default)
from langchain_agent_server import create_app, BearerAuth
async def agent_factory(token: str):
# token is the Bearer token from the Authorization header
# Use it to create a per-request agent with user context
return build_agent_for_user(token)
app = create_app(
title="My Agent API",
agent_factory=agent_factory,
auth=BearerAuth(), # this is the default
)
Returns 403 if no Authorization header is provided.
No Auth
from langchain_agent_server import create_app, NoAuth
async def agent_factory(auth_context):
# auth_context is always None
return my_agent
app = create_app(
title="My Agent API",
agent_factory=agent_factory,
auth=NoAuth(),
)
Custom Auth
Implement the AuthDependency protocol:
from fastapi import HTTPException
class ApiKeyAuth:
def __init__(self, valid_keys: set[str]):
self.valid_keys = valid_keys
async def __call__(self, authorization: str | None) -> str:
if authorization not in self.valid_keys:
raise HTTPException(status_code=401, detail="Invalid API key")
return authorization
app = create_app(
title="My Agent API",
agent_factory=agent_factory,
auth=ApiKeyAuth({"sk-secret-key-1", "sk-secret-key-2"}),
)
The auth callable receives the raw Authorization header and returns whatever your agent_factory needs.
Message Conversion
The package includes utilities for converting between OpenAI and LangChain message formats:
from langchain_agent_server import (
convert_openai_to_langchain_messages,
convert_langchain_to_openai_message,
)
# OpenAI format -> LangChain objects
messages = convert_openai_to_langchain_messages([
{"role": "user", "content": "Hello"},
{"role": "assistant", "content": "Hi!"},
])
# LangChain object -> OpenAI format dict
from langchain_core.messages import AIMessage
msg_dict = convert_langchain_to_openai_message(AIMessage(content="Hi!"))
# {"type": "ai", "role": "assistant", "content": "Hi!"}
Why?
LangChain's official options for serving agents are:
- LangServe — maintenance mode, no new features
- LangGraph Platform — paid managed service (self-hosted requires Enterprise license)
This package is a lightweight alternative: ~200 lines of code, zero vendor lock-in, MIT licensed. You own your infrastructure.
License
MIT
Metadata
Release files for langchain-agent-server 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| langchain_agent_server-0.1.0.tar.gz | 58.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| langchain_agent_server-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 68.0 kB
Release files / langchain_agent_server-0.1.0.tar.gz
| Download URL | langchain_agent_server-0.1.0.tar.gz |
|---|---|
| Size | 58.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
91323eab9afd592f796a08cbb04703f04e5f7256f0b3d9cf84fe76944b116984
|
|
BLAKE2b-256 checksum How to use checksums |
72d7208a2c9a9ec87944a16e706a255275318572a3001a7702d289e2fc3187a1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.13
|
Release files / langchain_agent_server-0.1.0-py3-none-any.whl
| Download URL | langchain_agent_server-0.1.0-py3-none-any.whl |
|---|---|
| Size | 9.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1a32d3bd3f4f2e43bc347adc9174e7981e4f4324200a8b7a9222950cc5a249e3
|
|
BLAKE2b-256 checksum How to use checksums |
4b16710d8d04de40fe483c891e7aa0d9204fd1ef887e108c49f14d90925f6ed1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.13
|