LangChain, LangGraph, Claude Agent SDK, OpenAI Agents SDK, Flue, Pydantic AI, and NVIDIA's NeMo Agent Toolkit — compared through a hybrid self-hosting lens, with the marketing stripped off.
The seven contenders aren't the same kind of thing. Some are integration glue. Some are orchestration graphs. A couple are full autonomous harnesses, and NVIDIA's NeMo Agent Toolkit sits off to the side of all of them. The study works through one deliberately concrete lens: a hybrid setup where your own GPU inference and cloud APIs both have to work.
No live GPU inference, no durability tests, no long-horizon quality claims — those need hardware and credentials we didn't have, and the study says so. What we did run is cheap and decisive: build the portability agent three ways, measure four supply chains, and crack open the one package that wouldn't sit the test.
You want cloud APIs today and your own GPUs tomorrow, without rewriting the agent in between. Build the same ~32-line tool-calling agent, point it at an OpenAI-compatible endpoint, and prove the exit is a config change — not a migration.
client = OpenAI( base_url="https://your-nim/v1" # ← the only line that moves )
We built the identical ~32-line tool-calling agent in LangGraph, the OpenAI Agents SDK, and Pydantic AI; each passed against an OpenAI-compatible endpoint with a one-line base-URL swap.
Boundary — passing means the wire protocol carries you, not that the frameworks' features are equal.
A "which framework?" debate that never converges, because the candidates are not the same kind of thing. Place each one on its layer — integration glue, orchestration graph, full harness, fleet layer — before you score a single feature, and most of the argument evaporates.
glue → orchestration → harness → fleet
# put each contender on a rung first
The study mapped all seven onto four layers and scored twelve decision axes; the maturity check cut exactly along layer lines — orchestration is past 1.0 with stability commitments, every harness and vendor SDK is not.
Boundary — layer tells you the kind of thing; it won't rank two tools on the same rung.
Two frameworks look equivalent until you install them and one brings 271 friends. Dependency count and install size are risk numbers you can get in minutes — pull them before the feature demo, not after, and they separate the field more sharply than any landing page.
uv pip install <framework> uv pip list | wc -l # deps · then du -sh
Measured on our bench: Sandcastle 7 packages in 15 MB · LangChain 34 in 95 MB · Flue 271 in 210 MB · pydantic-ai's full bundle 147 in 406 MB.
Boundary — install weight is a risk signal, not a quality verdict; small can still be underpowered.
A turnkey harness feels like the fast path — until self-hosting becomes a requirement in a contract. Find out which layer genuinely cannot point at your own endpoint before you standardize on it, by opening the package and reading what it actually wraps.
pip show claude-agent-sdk
# then look inside: what model paths does it allow?
We cracked open the Claude Agent SDK package and found a 248 MB compiled Claude Code binary whose supported model paths are Anthropic, Bedrock, and Vertex — the one contender that cannot take the base-URL test.
Boundary — sealed is also why it's the most battle-tested loop; lock-in concentrates where capability is densest.
| Play | Tier | Effort | Receipt |
|---|---|---|---|
| 01 Keep your exit one base_url away | Verified | AN AFTERNOON | 32-line agent in 3 frameworks · one-line base-URL swap |
| 02 Sort contenders by layer first | Verified | AN AFTERNOON | 7 → 4 layers · 12 axes · maturity cut along layer lines |
| 03 Weigh the supply chain before the demo | Verified | AN AFTERNOON | 7 pkg/15 MB → 271 pkg/210 MB across the field |
| 04 Probe the lock-in before you're in it | Verified | AN AFTERNOON | Claude Agent SDK: 248 MB binary · Anthropic/Bedrock/Vertex only |
If you run one play tonight, build the 32-line portability probe against your own stack. Point it at an OpenAI-compatible endpoint, swap the base_url, and run it. Now you know for certain whether your exit is a config change or a rewrite — instead of guessing from a docs page.
client = OpenAI(base_url="https://your-nim-endpoint/v1") # build it in LangGraph, OpenAI Agents SDK, or Pydantic AI — all passed