Swarms v16 'Overclock': Token Accounting, Decision Models, MCP Deployment, and Runs That Start Clean
Overclock is four and a half weeks of work: 114 commits between September 1 and October 2, 2026, across 228 files, +18,984 / −4,585 lines. More of those lines went into tests/ (+6,540) than into swarms/ itself (+6,409 / −3,803). The suite grew from 762 test functions to 944.
Zena (v14) made the framework observable. Akira (v15) made it honest. Overclock makes it measurable and fast.
Five threads run through this release.
The first is cost. Before Overclock, an Agent had no way to tell you what a run cost. The provider returned exact token counts on every completion, and the framework dropped them. Now agent.usage reports provider-billed input, output, cached and reasoning tokens. SwarmRouter, GraphWorkflow and HeavySwarm sum them across every agent they ran, including directors, aggregators and judges that are not in your agent list. agent.input_tokens tells you how big the next request will be before you send it. Streaming runs, which reported zero, are counted too.
The second is isolation. Akira converted every structure onto typed chat turns. Overclock found the next layer down: most structures built their Conversation once in __init__ and never reset it, so a second run() on the same instance served the previous task's transcript as context, and every batch entry point reused one instance. Twelve structures were fixed, plus three that shared one Agent across threads and two that collected results in completion order.
The third is failures that surface. When an LLM call failed every retry, Agent.run() returned an empty string or the prompt, with no error, and fallback_models were never tried. It now raises AgentLLMError. A failed tool is no longer blamed on the provider and re-run through the model. A total director outage in HierarchicalSwarm is no longer reported as a success. MCP tool errors under mcp 2.x are no longer reported as successes. Each of these returned a plausible value instead of an error.
The fourth is new capabilities: DecisionModel for typed, calibrated decisions from TypeSafe's Jev and Cloudflare's Clef; MCPDeployer, which serves any agent or swarm as an authenticated MCP server; TreeOfThoughts; SwarmRouter(fallback_swarms=...); conversations seeded from messages; skills from several directories; and a glob tool for the autonomous harness.
The fifth is speed, which is where the name comes from. Tool handling moved out of agent.py into a new ToolManager (agent.py went from 4,566 to 3,227 lines), and the context compressor stopped tokenizing the whole history on every loop. A tool turn is about 5× faster on a short conversation and about 65× faster on a long one.
This post covers all of it. New features and improvements first, then a day-by-day, commit-by-commit record of the entire release.
Getting the Update
# pip
pip install -U swarms
# uv
uv pip install -U swarms
# uv, in a project managed by uv
uv add swarms --upgrade
# poetry
poetry add swarms@latest
# pdm
pdm update swarms
Pin it if you want reproducible installs:
pip install "swarms==16.0.0"
uv pip install "swarms==16.0.0"
Confirm what you got. swarms.__version__ is new in this release:
python -c "import swarms; print(swarms.__version__)"
If you are coming from v15, read Breaking Changes first. Three defaults changed (temperature, dynamic_tools and MCPConnection.transport), a failed LLM call now raises instead of returning, and the tool methods moved from Agent to agent.tool_manager.
New Features
Token accounting: agent.usage, router.usage, and agent.input_tokens
The headline capability of Overclock. The provider returns exact token counts on every completion, and LiteLLM normalises them to one shape for every provider. Until this release they were discarded, so there was no way to ask an agent what a run had cost.
from swarms import Agent
agent = Agent(agent_name="Analyst", model_name="gpt-5.4", max_loops=1)
agent.input_tokens # size of the next request: system prompt, memory, tool schemas
agent.run("Summarise the latest FOMC statement in three bullets.")
agent.usage
# {'input_tokens': 1204, 'output_tokens': 87, 'cached_tokens': 1024,
# 'reasoning_tokens': 0, 'total_tokens': 1291}
usage is summed over every LLM call the agent has made, including the tool-summary call each loop makes. cached_tokens is the part of the input served from the provider's prompt cache, and reasoning_tokens is the part of the output a reasoning model spent thinking. The property returns a copy, so callers cannot corrupt the running total. Take a snapshot before and after a run to isolate one run's cost from the agent's lifetime total.
input_tokens answers a different question: how big the agent's next request will be, measured with count_tokens under the agent's own model_name, so the tokenizer matches the model. It counts the system prompt, the whole short_memory and any tool schemas, and it costs no API call.
Three gaps were closed after the first version landed (#2190):
- Streaming runs reported zero. A stream carries no usage unless you ask for it. The wrapper now sends
stream_options={"include_usage": True}and records the provider's trailing usage-only chunk. - Reasoning tokens were invisible. In the PR's example, 11 of a reply's 23 output tokens were reasoning. They are now reported separately.
- OpenAI's reasoning-era models rejected the request.
o1/o3/o4and thegpt-5family refusemax_tokensand wantmax_completion_tokens, anddrop_paramscould not help because litellm lists both keys as supported. The wrapper now sends the key each model accepts.
The same shape works one level up, on every structure that owns agents:
from swarms import Agent, SwarmRouter
agents = [
Agent(agent_name="Researcher", model_name="gpt-5.4-mini", max_loops=1),
Agent(agent_name="Writer", model_name="gpt-5.4-mini", max_loops=1),
]
router = SwarmRouter(agents=agents, swarm_type="HierarchicalSwarm")
router.run("Write a one-page brief on HBM supply constraints.")
router.usage # summed over every agent the swarm ran, director included
router.usage (#2160) adds up your agents and any agent a built swarm holds on its own: a HierarchicalSwarm director, a MixtureOfAgents aggregator, a judge. Those are not in router.agents, but their spend is part of the run. Agents are deduplicated by identity, so one that appears in both places is counted once.
GraphWorkflow.usage (#2365) collects agents once across the whole graph, including subgraphs, so an agent that backs two nodes is counted once. HeavySwarm.usage (#2366) also counts question generation, which runs on a bare LiteLLM rather than an Agent and so was in nobody's Agent.usage. Runnable walkthroughs are in examples/single_agent/utils/agent_usage.py and examples/multi_agent/swarm_router/swarm_router_usage.py.
DecisionModel: typed, calibrated decisions from TypeSafe and Cloudflare
A new kind of model joins the framework. A decision model does not generate text. It answers typed questions about a state with calibrated probabilities, so your code can branch on the answer and on how sure the model is.
| Question type | Asks | Returns |
|---|---|---|
| Choice | Pick one option from a set | choice ,probabilities ,confidence |
| Score | Rate against ordered levels | score ,legend ,probabilities ,confidence |
| Noul | Is this statement true? | noul , from 0 to 1 |
from swarms import DecisionModel, get_decision_models
model = DecisionModel() # TypeSafe Jev, reads TYPESAFE_API_KEY
clef = DecisionModel(model_name="clef") # Cloudflare Workers AI
result = model.run(
state={"message": "Checkout has been failing for every customer for an hour."},
questions={
"team": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {"billing": "Payments", "technical": "Outages and errors"},
},
"urgent": {"type": "noul", "instructions": "Is this urgent?"},
},
)
answers = result["answers"]
if answers["team"]["confidence"] < 0.5:
print("Send to a human.")
print(answers["team"]["choice"], answers["urgent"]["noul"])
get_decision_models()
# ['jev-latest', 'jev-preview', 'jev-1.13.0', 'clef', 'clef-flash']
Every question is answered in one request, so a router, a guardrail and a scorer cost one call between them. TypeSafe's jev-latest is the default. Switching to Cloudflare's Clef is only a model-name change: names starting with clef route to Workers AI, read CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_AUTH_TOKEN from the environment or .env, and unwrap Workers AI's result envelope, so you get the same response shape either way. get_decision_models() merges the built-in names with each provider's live model list when its credentials are set.
There are single-question helpers (choice, score, noul), an async arun, retries with backoff that honour retry-after, and checks on both ends: questions are validated before they are sent, and every question must come back with an answer of its own type. Another provider plugs in through base_url, endpoint and api_key_env, or by overriding build_headers, build_payload and parse_response. The client calls the HTTP APIs directly with httpx, so there is no new dependency.
Nine examples ship in examples/decision_models/, including a decision model as the director of a HierarchicalSwarm, a 500-resume screening funnel that sends only the shortlist to LLM agents, a calibrated judge for a model ensemble, a debate referee that stops when no new arguments appear, and guardrails on a GroupChat (#2416).
MCPDeployer: serve any agent or swarm as an authenticated MCP server
Swarms could consume MCP servers. It could not easily be one. MCPDeployer turns one or more agents, swarms or callables into an MCP server with an auth layer in front of it.
from swarms import Agent, MCPDeployer
agent = Agent(agent_name="Researcher", model_name="gpt-5.4", max_loops=1)
MCPDeployer(agent, api_keys=["sk-local-dev"], port=8000).run()
Another agent connects to it like any other MCP server:
from swarms import Agent, MCPConnection
client = Agent(
agent_name="Client",
model_name="gpt-5.4",
max_loops=1,
mcp_url=MCPConnection(url="http://127.0.0.1:8000/mcp", api_key="sk-local-dev"),
)
The first argument is one target, a list, or a dict of tool name to target. A target is an Agent, any structure with a run() method (SequentialWorkflow, SwarmRouter, ...), or a plain callable. Each becomes its own MCP tool with a (task, img) schema. Auth is layered: a custom auth callable, a token_verifier for OAuth, or static api_keys / api_key_env, with allow_anonymous and public_paths for the cases that need them. Duplicate names and unservable entries raise at construction, not at first call (#2219).
timeout= now really times out a blocking target (#2374). anyio.to_thread.run_sync shields its wait from cancellation unless told otherwise, so the deadline fired, the wait ignored it, and the late result came back as a success. Examples are in examples/mcp/mcp_deployer/, and both READMEs gained a "Serve an Agent as an MCP Server" section.
TreeOfThoughts: search instead of answering in one pass
An implementation of Tree of Thoughts (Yao et al., 2023) joins swarms.agents. Instead of answering in one pass, it grows a tree of partial solutions and searches it.
from swarms import TreeOfThoughts
agent = TreeOfThoughts(
model_name="gpt-5.4",
search_algorithm="bfs", # or "dfs", with backtracking
generation_strategy="propose", # or "sample", one call per candidate
evaluation_strategy="value", # or "vote", compare candidates
thought_description="One arithmetic operation on two remaining numbers.",
evaluation_criteria="Can the numbers left still reach exactly 24?",
)
answer = agent.run("Use 4, 9, 10 and 13, each once, with + - * / to make 24.")
agent.last_result.steps # the best path; .root is the whole tree
agent.usage # tokens across every call the search made
Four steps repeat: generate num_thoughts candidate next steps, score each from 0 to 1 and prune below value_threshold, search breadth-first with a beam of breadth or depth-first with backtracking, and write the final answer from the best path. max_expansions caps the cost.
Every model output is a function call validated against a Pydantic schema, with $ref inlined, because with nested $ref fields Claude Haiku 4.5 invented its own field names and every generator call failed validation. Each call runs on a fresh, stateless Agent, so evaluations never see each other's context, and calls at the same depth run concurrently. The prompts contain nothing task-specific: thought_description and evaluation_criteria adapt the search to a domain (#2379).
SwarmRouter(fallback_swarms=...): try the next architecture when one fails
SwarmRouter ran exactly one swarm type. If it raised, the run was over, and a caller wanting HierarchicalSwarm with SequentialWorkflow behind it had to build two routers and hand-write the retry.
from swarms import SwarmRouter
router = SwarmRouter(
agents=agents,
swarm_type="HierarchicalSwarm",
fallback_swarms=["SequentialWorkflow", "ConcurrentWorkflow"], # tried in order
)
result = router.run(task)
router.active_swarm_type # which swarm actually served the run
router.fallback_attempts # [{"swarm_type": ..., "error": ...}] for each that failed
(Excerpt of release notes; full changelog at canonical URL.)