let result = try await Workflow()
.step(researchAgent)
.step(writerAgent)
.run("Summarize the latest WWDC session on Swift concurrency.")Two agents, one pipeline, compiled to a DAG. Crash recovery is opt-in β enable the Integrations trait and durable checkpointing β and Swift concurrency safety is enforced at compile time.
Default link is lean: core Swarm + on-device Foundation Models. Graph/memory/web/Hive paths are trait-gated (off by default) and are not linked into Swarm unless you enable Integrations.
HiveCore, Membrane, and ContextCore are native in-tree Sources/ targets (internal modules β not separate library products). Enabling Integrations links those modules plus Wax (still remote) and SwiftSoup; omitting the trait does not link them into Swarm. Lean resolve never pulls Hive/Membrane/ContextCore/Conduit package identities, and (with trait-gated product edges) also does not pin Wax, MetalANNSβGRDB, swift-crypto, swift-mutex, or SwiftSoup. Default remotes remain (swift-syntax via the default-on Macros trait, swift-log, MCP sdk, OTel, plus NIO transitives β including swift-collections via NIO). Disable Macros with traits: [] to drop swift-syntax; use FunctionTool instead of @Tool. SWARM_CORE_ONLY=1 drops the integration package block entirely. ContextCore / full Membrane session stack require Apple platforms (Metal/CoreML); Linux Integrations still builds Hive + MembraneCore + web helpers. DefaultAgentMemory uses a CoreML MiniLM model that is not bundled β call SemanticEmbeddingAvailability.ensureModelAvailable() to download it on demand. Without the model, ContextCore falls back to deterministic pseudo-embeddings, logs a once-per-process warning naming that API, and DefaultAgentMemory.isSemanticMemoryAvailable reports false.
Root-package note: bare swift build / swift test on this repo compile every registered target, so integration modules need either --traits Integrations or the lean CI helper (scripts/ci/lean-build-test.sh). App consumers only build reachable targets and stay lean without that helper.
// Lean default link (recommended for most apps). Macros are on by default.
.package(url: "https://github.com/christopherkarani/Swarm.git", from: "0.6.2")
// Full integrations: durable Hive workflows, ContextCore+Wax default memory,
// Membrane adapters, and web helpers. Integrations also enables Macros.
.package(
url: "https://github.com/christopherkarani/Swarm.git",
from: "0.6.2",
traits: ["Integrations"]
)
// Macro-free lean: drops swift-syntax. You lose @Tool, @Parameter, #Prompt,
// and @Traceable β use FunctionTool instead.
.package(
url: "https://github.com/christopherkarani/Swarm.git",
from: "0.6.2",
traits: []
)// FunctionTool compiles without the Macros trait
let echo = FunctionTool(
name: "echo",
description: "Echoes a message",
parameters: [
ToolParameter(name: "message", description: "Text to echo", type: .string)
]
) { args in
let message = try args.require("message", as: String.self)
return .string(message)
}From a checkout of this package:
# Lean (root package): product-scoped build, or scripts/ci/lean-build-test.sh
swift build --product Swarm --product SwarmMCP --product SwarmOpenTelemetry \
--product SwarmMembrane --product SwarmCapabilityShowcase
# SwarmMembrane is a deprecated hollow re-export of Swarm; import Swarm
# instead. The product will be removed in 0.7.0.
# Full graph
swift build --traits Integrations
swift test --no-parallel --traits Integrations
swift run --traits Integrations SwarmCapabilityShowcase matriximport Swarm
// The @Tool macro generates the JSON schema at compile time
@Tool("Looks up the current stock price")
struct PriceTool {
@Parameter("Ticker symbol") var ticker: String
func execute() async throws -> String { "182.50" }
}
// Create an agent with unlabeled instructions first and tools in the trailing @ToolBuilder closure
// Built-in backend: Apple Foundation Models (no API key on supported devices)
let agent = try Agent("Answer finance questions using real data.",
configuration: .default.name("Analyst"),
inferenceProvider: .foundationModels()) {
PriceTool()
CalculatorTool()
}
let result = try await agent.run("What is AAPL trading at?")
print(result.output) // "Apple (AAPL) is currently trading at $182.50."That is a working agent with type-safe tool calling. Swarm also supports AGENTS.md and SKILL.md for declarative agent specs and reusable skills β see the Getting Started guide for the full workspace layout.
- Swift concurrency is part of the surface. Swift 6.2
StrictConcurrencyis enabled across the package. - Tools stay type-safe. The
@Toolmacro generates JSON schemas from Swift structs. - Workflows can survive crashes. Durable checkpointing (Integrations trait) lets you resume from an explicit checkpoint ID.
- Built-in inference is on-device Foundation Models, plus an OpenAI-compatible remote provider. Linux and machines without Apple Intelligence use
.openAICompatible(...); the agent loop stays the same. - It is written in Swift all the way down.
AsyncThrowingStream, actors, result builders, and macros are first-class here.
Swarm now ships with an in-repo capability showcase that exercises the stable surface area in one deterministic matrix:
- agents and tools
- streaming
- conversation plus session persistence
- sequential, parallel, routed, and repeat-until workflows
- handoffs
- memory
- on-device workspace loading
- guardrails
- resilience helpers
- durable checkpoint and resume
- observability
- MCP discovery and tool bridging
- provider selection
Run it locally:
# Capability showcase matrix covers durable workflows; enable Integrations
swift run --traits Integrations SwarmCapabilityShowcase list
swift run --traits Integrations SwarmCapabilityShowcase matrix
swift run --traits Integrations SwarmCapabilityShowcase run handoff
swift run --traits Integrations SwarmCapabilityShowcase smokeThe deterministic matrix is CI-safe. Live-provider smoke coverage is opt-in through environment variables. See docs/guide/capability-showcase.md for the scenario catalog and smoke-mode details.
Two minimal, buildable apps under Examples/ stress the public API:
| Example | What it proves |
|---|---|
Examples/OnDeviceChat |
Foundation Models chat with @Tool, streaming, and multi-turn Conversation (zero API keys; --demo for CI) |
Examples/MultiAgentPipeline |
Sequential + parallel workflows and durable checkpoint/resume (--demo for CI; requires Integrations) |
Examples/WaxChat |
Wax durable memory + websearch chat (--demo for CI; requires Integrations) |
Examples/CodeReviewer |
Lightweight CLI that links Swarm and prints a deterministic review plan |
cd Examples/OnDeviceChat && swift run OnDeviceChat --demo
cd Examples/MultiAgentPipeline && swift run MultiAgentPipeline --demo
cd Examples/WaxChat && swift run WaxChat --demoPackage-root demo executables are opt-in so the default library graph stays focused on the framework products:
SWARM_INCLUDE_DEMO=1 swift build
SWARM_INCLUDE_DEMO=1 swift run SwarmDemo
SWARM_INCLUDE_DEMO=1 swift run SwarmMCPServerDemoFor Apple platforms, use the built-in on-device path β no API keys:
import Swarm
// Requires macOS/iOS 26+ and Apple Intelligence available on the device.
let agent = try Agent(
"You are a private on-device assistant.",
inferenceProvider: .foundationModels()
) {
// @Tool structs or FunctionTool values
}
let result = try await agent.run("Summarize my notes.")Notes that matter in production:
- Availability: use
FoundationModelsInferenceProvider.ifAvailable()or checkFoundationModelsInferenceProvider.isAvailablebefore assuming the system model is ready. - Tool calling: Swarm bridges
@Tool/ToolSchemato Apple'sFoundationModels.Tooland executes tools in the agent loop with guardrails intact (capture mode, default β all tool calls from a parallel group are recovered). Opt in to experimental native session mode for Apple's inner tool loop and token streaming with tools. - Streaming tool calls: not advertised as token-level tool streaming;
Agent.streamobserves the samerunloop viaAgentEvent(lifecycle, tools, and.output(.token)chunks). Foundation Models yields incremental text deltas; providers without a streaming API emit the full response as a single chunk. - Structured outputs:
runStructureduses Foundation Models guided generation when the JSON Schema maps ontoGenerationSchema(source: .providerNative); otherwise it is prompt instruction + parse (source: .promptFallback)..jsonObjectalways uses the fallback path. - Dynamic profiles:
.foundationModels(profile:)re-resolves instructions/tools/history every turn (WWDC 2026βaligned Swarm API). - Linux / CI: Foundation Models is compile-time gated. Use
.openAICompatible(.ollama(model:))(or any OpenAI-compatible host), inject a mock, or use the deterministic--demomodes inExamples/.
No Apple Intelligence required. Same agent loop, URLSession only:
// Local (Ollama). Data stays on loopback HTTP β not on-device Foundation Models.
let local = try Agent(
"Be helpful.",
inferenceProvider: .openAICompatible(.ollama(model: "llama3.2"))
)
// Cloud. Prompt content leaves the device.
let cloud = try Agent(
"Be helpful.",
inferenceProvider: .openAICompatible(
.openAI(apiKey: "sk-...", model: "gpt-4o")
)
)| Host | Factory | Leaves the device? |
|---|---|---|
| OpenAI | .openAI(apiKey:model:) |
Yes β to OpenAI |
| Azure OpenAI | .azureOpenAI(resource:deployment:apiKey:) |
Yes β to Azure |
| OpenRouter | .openRouter(apiKey:model:) |
Yes β to OpenRouter |
| Ollama | .ollama(model:) |
Yes β to localhost HTTP |
| LM Studio | .lmStudio(model:) |
Yes β to localhost HTTP |
See Remote Providers for full snippets, structured-output honesty, and live Ollama test setup.
// WebSearchTool requires the Integrations trait and an API key
// (lean builds compile this initializer, warn immediately, and throw on execute).
let researcher = try Agent("Research the topic and extract key facts.",
inferenceProvider: .foundationModels()) {
WebSearchTool(apiKey: "YOUR_API_KEY")
}
let writer = try Agent("Write a concise summary from the research.",
inferenceProvider: .foundationModels())
let result = try await Workflow()
.step(researcher)
.step(writer)
.run("Latest advances in on-device ML")Each agent resolves its own provider. Pass inferenceProvider: per agent (as above), or call await Swarm.configure(provider: myProvider) once at app startup to share a default across every agent that doesn't specify one.
let result = try await Workflow()
.parallel([bullAgent, bearAgent, analystAgent], merge: .structured)
.run("Evaluate Apple's Q4 earnings.")
// Three perspectives, merged into one output.let result = try await Workflow()
.route { input in
if input.contains("$") { return mathAgent }
if input.contains("weather") { return weatherAgent }
return generalAgent
}
.run("What is 15% of $240?")Agent.stream runs the same agent loop as run, forwarding AgentEvent values through an observer. You get lifecycle, tool, and output events as they happen β not a separate token decoder.
.output(.token) is an incremental text chunk when the provider streams (Foundation Models does). If the provider only has a completion API, that event is the full response in one chunk. Tool calls still complete as capture-then-execute turns unless the provider implements tool-call streaming.
for try await event in agent.stream("Summarize the changelog.") {
switch event {
case .output(.token(let t)): print(t, terminator: "")
case .tool(.completed(let call, _)): print("\n[tool: \(call.toolName)]")
case .lifecycle(.completed(let r)): print("\nDone in \(r.duration)")
case .lifecycle(.failed(let error)): print("\nError: \(error)")
default: break // Other events include .output(.thinking(...)), .handoff(...), .observation(...), and .lifecycle(.iterationStarted(...)).
}
}More examples
let agent = try Agent("You remember past conversations.",
memory: .vector(embeddingProvider: myEmbedder, similarityThreshold: 0.75),
inferenceProvider: .foundationModels()) {
// tools
}let agent = try Agent("You are a helpful assistant.",
inputGuardrails: [InputGuard.maxLength(5000), InputGuard.notEmpty()],
outputGuardrails: [OutputGuard.maxLength(2000)])let reverse = FunctionTool(
name: "reverse",
description: "Reverses a string",
parameters: [ToolParameter(name: "text", description: "Text to reverse", type: .string, isRequired: true)]
) { args in
let text = try args.require("text", as: String.self)
return .string(String(text.reversed()))
}
let agent = try Agent("Text utilities.") {
reverse
}let workflow = Workflow()
.step(monitor)
.durable.checkpoint(id: "monitor-v1", policy: .everyStep)
.durable.checkpointing(.fileSystem(directory: checkpointsURL))
let resumed = try await workflow.durable.execute("watch", resumeFrom: "monitor-v1")A mid-step crash re-runs that whole step. File stores keep the newest 16
checkpoints per run (configurable) and identify steps by kind, position, and
optional signature: β not source line numbers. See
Durable Execution.
// Built-in: on-device Foundation Models (no API key)
let local = try Agent("Be helpful.", inferenceProvider: .foundationModels())
// Built-in: OpenAI-compatible remote / local HTTP (Linux-friendly)
let remote = try Agent(
"Be helpful.",
inferenceProvider: .openAICompatible(.ollama(model: "llama3.2"))
)
// Custom backend: any type conforming to InferenceProvider
let custom = try Agent("Be helpful.", inferenceProvider: myCustomProvider)
// Or swap at runtime via environment
let modified = agent.environment(\.inferenceProvider, myCustomProvider)let conversation = Conversation(with: agent)
let response1 = try await conversation.send("What's the weather?")
let response2 = try await conversation.send("And tomorrow?") // Context preserved
for message in await conversation.messages {
print("\(message.role): \(message.text)")
}| Swarm | LangChain | AutoGen | |
|---|---|---|---|
| Language | Swift 6.2 | Python | Python |
| Data race safety | Compile-time | Runtime | Runtime |
| On-device LLM | Foundation Models | n/a | n/a |
| Execution model | Typed Workflow graph |
Loop-based | Loop-based |
| Crash recovery | Checkpoints (Integrations) | n/a | Partial |
| Type-safe tools | @Tool macro (compile-time) |
Decorators (runtime) | Runtime |
| Streaming | AsyncThrowingStream |
Callbacks | Callbacks |
| iOS / macOS native | First-class | n/a | n/a |
| Agents | Agent struct with @ToolBuilder trailing closure, AgentRuntime protocol |
| Workflows | Workflow: .step(), .parallel(), .route(), .repeatUntil(), .timeout() |
| Tools | @Tool macro, FunctionTool, @ToolBuilder, parallel execution |
| Memory | .conversation(maxMessages:), .vector(embeddingProvider:similarityThreshold:maxResults:), .slidingWindow(maxTokens:), .summary(configuration:summarizer:), .hybrid(configuration:summarizer:) |
| Guardrails | InputGuard.maxLength(), InputGuard.notEmpty(), InputGuard.custom(), OutputGuard.maxLength(), OutputGuard.custom() |
| Conversation | Conversation actor for stateful multi-turn dialogue |
| Resilience | 7 backoff strategies, circuit breaker, fallback chains, rate limiting |
| Observability | AgentObserver, Tracer, SwiftLogTracer, per-agent token metrics when the provider reports usage (Foundation Models does not) |
| MCP | Model Context Protocol client and server support |
| Providers | Built-in Apple Foundation Models (on-device) and OpenAICompatibleProvider (OpenAI / Azure / OpenRouter / Ollama / LM Studio); inject any InferenceProvider for other backends |
| Macros | @Tool, @Parameter, @Traceable, #Prompt |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your Application β
β iOS 26+ Β· macOS 26+ Β· Linux (Ubuntu 22.04+) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Workflow Β· Conversation Β· .run() Β· .stream() β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Agents Memory Tools β
β Agent (struct) Memory factories @Tool macro β
β AgentRuntime Conversation FunctionTool β
β (dot-syntax) @ToolBuilder β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β InputGuard Β· OutputGuard Β· Resilience Β· Observability Β· MCPβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Durable Graph Runtime (internal) β
β Workflow Graph Β· Checkpointing Β· Deterministic retry β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β InferenceProvider (pluggable) β
β Foundation Models Β· OpenAI-compatible Β· custom provider β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Platform | Minimum |
|---|---|
| Swift | 6.2+ |
| iOS | 26.0+ |
| macOS | 26.0+ |
| tvOS | 26.0+ |
| Linux | Ubuntu 22.04+ with Swift 6.2 |
The default Swarm graph is CI-tested on Ubuntu with Swift 6.2. Apple-only features such as Foundation Models, SwiftData, OSLog, and some built-in tool behavior are unavailable or different on Linux; use OpenAICompatibleProvider (stubbed in CI, live against Ollama when SWARM_OLLAMA_LIVE_TESTS=1) or inject a mock.
| Getting Started | Installation, first agent, workflows |
| Remote Providers | OpenAI-compatible provider (OpenAI, Azure, OpenRouter, Ollama, LM Studio) |
| OpenTelemetry Tracing | OTLP/HTTP JSON export of agent and LLM spans, plus W3C traceparent on outbound HTTP |
| API Reference | Every type, protocol, and API |
| Front-Facing API | Public API surface |
| Why Swarm? | Design philosophy and architecture |
- Fork β branch β
swift testβ PR - All public types must be
Sendable; the compiler enforces it - Format with
swiftformat Sources Tests --lint --config .swiftformat
Bug reports and feature requests: GitHub Issues
GitHub Issues Β· Discussions Β· @ckarani7
If Swarm saves you time, a star helps others find it.
Released under the MIT License.
