Managoat is the hosted Fountain. Fountain is the open-source engine, and its name is on the CLI, the API, the SDK and this manual. Everything here applies to Managoat unless a page says it is for a self-hosted server.
LangChain and Deep Agents
LangChain is the agent framework, and Deep Agents is its harness for an orchestrator that plans and delegates to subagents. Fountain fits there as a subagent. The orchestrator plans. A Fountain agent does the work in a sandbox of its own, with its own repositories and credentials, and reports back once.
Deep Agent (LangGraph) ──task tool──▶ FountainAgent runnable ──HTTPS──▶ Fountain ──▶ sandbox
plans, reads reports POST /v1/chat/completions /v1 the Fountain agent
X-Fountain-Thread: <thread_id>:<agent>
It rides on the OpenAI-compatible API, so it is
alpha, behind the openai_compat flag. There is no package to install from
us. One file, fountain_langchain.py, is the whole integration, and the
example ships it.
Summary
| Direction | Inbound. LangChain drives Fountain. |
| Talks over | OpenAI chat completions, at POST /v1/chat/completions. |
| Configured on | Your LangChain code. |
| Plugin | None. One Python file, in the example. |
| Credential | A Fountain API key, as the bearer token. |
| Scope | One LangGraph thread_id is one sandbox per Fountain agent. |
| Status | Alpha, with the endpoint under it. Read Feature status. |
Set it up
Make an API key.
fountain keys create langchain
Clone the example and install it.
git clone https://github.com/BinaryBourbon/fountain
cd fountain/examples/deepagents-contractor
pip install -r requirements.txt
export FOUNTAIN_TOKEN=ftn_...
export ANTHROPIC_API_KEY=sk-ant-... # the orchestrator's model, not Fountain's
Run it.
python main.py --list # the agents on your account
python main.py -a reflex-1 -a pr-reviewer \
"Ask pr-reviewer to review the open PRs on jhgaylor/rounds, then summarise."
The orchestrator is an ordinary model that returns tool calls. Each -a names a Fountain
agent it can delegate to. --thread keeps the same sandboxes on a second
run.
Three shapes
FountainAgent(name) has three shapes that treat the agent as a leaf, and a
fourth that makes it the model. The three send one prompt, wait for the turn,
and return the text.
A Deep Agents subagent, for create_deep_agent.
from deepagents import create_deep_agent
from fountain_langchain import FountainAgent
agent = create_deep_agent(
model="anthropic:claude-sonnet-5",
subagents=[
FountainAgent("pr-reviewer").as_subagent(
"Reviews and fixes pull requests. Its sandbox has the repository."
),
],
)
agent.invoke({"messages": [("user", "...")]}, {"configurable": {"thread_id": "t1"}})
A tool, for create_agent or for a loop of your own.
from langchain.agents import create_agent
agent = create_agent(model="anthropic:claude-sonnet-5",
tools=[FountainAgent("pr-reviewer").as_tool()])
A runnable, for a graph of your own. The input and the output both hold
messages, which is the shape a Deep Agents subagent must have.
runnable = FountainAgent("pr-reviewer").as_runnable()
The thread
The thread key is what keeps a conversation in one sandbox. The runnable reads
the LangGraph thread_id from the ambient config and appends the agent's
name. Thus one Deep Agents thread holds one sandbox per Fountain agent,
across turns and across runs. Pass thread="..." to fix it yourself. Without
either, the FountainAgent makes one random key and keeps it.
A busy thread is a 409 with Retry-After. The runnable waits and sends
again. Fountain does not queue a second prompt behind a turn that is in
progress.
A Fountain agent as the model
The fourth shape puts a Fountain agent inside the loop. create_agent
needs a model that returns tool calls. Fountain returns the calls to the
tools that you pass on the request, and runs its own tools in the sandbox
(Your tools). So ChatOpenAI with the
Fountain base URL is a model, and your LangChain tools run on your side.
from langchain.agents import create_agent
from langchain_core.tools import tool
from fountain_langchain import FountainAgent
@tool
def lookup_order(id: str) -> str:
"""Find an order by id."""
return orders[id]
agent = create_agent(model=FountainAgent("support").as_model(), tools=[lookup_order])
agent.invoke({"messages": [("user", "Where is order A-17?")]})
as_model() is ChatOpenAI(base_url=..., model=...) with the thread header
set, so the loop stays in one sandbox. Two caveats.
langchain-openaidropsreasoning_content, the field Fountain streams while a sandbox provisions. A first turn looks silent for a minute. The three shapes above use the stockopenaiclient and print those stages to stderr.- The agent's own tools do not come back as tool calls. Only the tools you pass do. A tool call that you do not answer in five minutes returns an error to the agent, and the turn continues.
What it does not do
- A sandbox backend for Deep Agents. That protocol needs
executeinside the sandbox, and Fountain's unit is a conversation, not a shell. - A package on PyPI. The file is small enough to copy.
The example is
examples/deepagents-contractor.