Skip to content
Β 
Β 

Repository files navigation

Swarm β€” Swift Agent Framework

Swift 6.2 Platforms License: MIT SPM

⭐ If Swarm is useful to you, a star helps others find it.

Swarm

Multi-agent orchestration for Swift β€” built for production, not demos.

let result = try await (fetchAgent --> reasonAgent --> writerAgent)
    .run("Summarize the WWDC session on Swift concurrency.")

print(result.output)

Three agents. One line. Crash-resumable, data-race-safe, compiled to a DAG.


Why Swarm

Most agent frameworks are Python-first, stringly-typed, and assume every workflow completes in one shot. Swarm makes three different bets.

1. Data races are compile errors

Swift 6.2's StrictConcurrency is enabled across every Swarm target β€” agents, memory systems, orchestrators, macros, and tests. Non-Sendable types crossing actor boundaries is a build failure, not a 3 AM incident.

// ❌ Compile error under Swarm's StrictConcurrency β€” caught before it ships
struct BrokenAgent: AgentRuntime {
    var cache: NSCache<NSString, NSString>
    // error: stored property 'cache' of 'Sendable'-conforming struct
    //        has non-Sendable type 'NSCache<NSString, NSString>'
}

// βœ“ Actor isolation makes shared state safe by construction
actor ResponseCache {
    private var store: [String: String] = [:]
    func set(_ value: String, for key: String) { store[key] = value }
    func get(_ key: String) -> String? { store[key] }
}

2. Workflows survive crashes

Swarm ships a WorkflowCheckpointStore protocol with two built-in implementations: InMemoryWorkflowCheckpointStore for tests and FileSystemWorkflowCheckpointStore for production. Each step boundary writes a snapshot. A pipeline that crashes on step 7 of 10 resumes from step 7 β€” not from the beginning.

// Crash-recovery checkpoint store β€” survives process restarts
let store = FileSystemWorkflowCheckpointStore(
    directory: .applicationSupportDirectory.appending(path: "workflow-checkpoints")
)

// Snapshots are serialised JSON β€” human-readable, diff-friendly
// { "workflowID": "run-42", "stepIndex": 3, "intermediateOutput": "...", "timestamp": "..." }

3. Orchestration is a DSL, not a switch statement

Eleven composable step types in a SwiftUI-style @resultBuilder. Sequential chains use -->. Type-safe pipelines use >>>. Dependency DAGs use .dependsOn(). Human review gates use a three-way response.

// Sequential chain
fetchAgent --> analyzeAgent --> writerAgent

// Type-safe pipeline (compiler enforces Input/Output types at each stage)
let pipeline = Pipeline<String, [String]>  { $0.components(separatedBy: "\n") }
             >>> Pipeline<[String], String> { $0.filter { !$0.isEmpty }.joined(separator: "β€’ ") }

// Dependency DAG β€” nodes run as soon as their dependencies complete
DAGWorkflow(nodes: [
    DAGNode("fetch",   agent: fetchAgent),
    DAGNode("refs",    agent: refsAgent),
    DAGNode("analyze", agent: analyzeAgent).dependsOn("fetch", "refs"),
    DAGNode("write",   agent: writerAgent).dependsOn("analyze"),
])

// Human-in-the-loop: three-way response
HumanApproval("Approve before publishing?", handler: reviewerUI)
// .approved          β†’ workflow continues with current output
// .rejected(reason)  β†’ workflow throws OrchestrationError.humanApprovalRejected
// .modified(newInput) β†’ corrected value flows forward, replacing the agent's output

How Swarm Compares

Swarm LangChain (Python) AutoGen
Language Swift 6.2 Python Python
Data race safety Compile-time βœ“ Runtime Runtime
On-device LLM Foundation Models βœ“ ❌ ❌
DAG execution engine Hive (compiled) βœ“ Loop-based Loop-based
Crash-resumable workflows βœ“ FileSystemCheckpointStore ❌ Partial
Type-safe tool parameters βœ“ @Tool macro Decorators (runtime) Runtime
Streaming AsyncThrowingStream βœ“ Callbacks Callbacks
iOS support βœ“ iOS 26+ ❌ ❌

Quick Start

import Swarm

// 1. Define a tool
@Tool("Returns the current price of a stock")
struct StockPriceTool {
    @Parameter("Ticker symbol, e.g. AAPL")
    var ticker: String

    func execute() async throws -> String {
        // call your price API here
        return "182.50"
    }
}

// 2. Define an agent blueprint
struct FinanceAgent: AgentBlueprint {
    let analyst = Agent(
        name: "Analyst",
        tools: [StockPriceTool()],
        instructions: "Answer finance questions concisely using real data."
    )

    @OrchestrationBuilder var body: some OrchestrationStep {
        Guard(.input) {
            InputGuard("no_pii") { input in
                input.contains("SSN") ? .tripwire(message: "PII detected") : .passed()
            }
        }
        analyst
    }
}

// 3. Run it
let result = try await FinanceAgent()
    .environment(\.inferenceProvider, .anthropic(key: "YOUR_API_KEY"))
    .run("What is the current price of AAPL?")

print(result.output)

Features

Capability Detail
Agents Agent (tool-calling), ReActAgent (thought→action→observation), PlanAndExecuteAgent (plan→execute→replan)
DSL @OrchestrationBuilder, --> sequential chain, >>> typed pipeline, .dependsOn() DAG
Step types Sequential, Parallel, DAGWorkflow, Router, RepeatWhile, Branch, Guard, Transform, Pipeline, SequentialChain, ParallelGroup
Memory Conversation (rolling buffer), VectorMemory (SIMD cosine via Accelerate), SummaryMemory (LLM-compressed), HybridMemory
Hive runtime Compiled DAG execution, channel checkpointing, deterministic retry (jitter stripped by RetryPolicyBridge)
Macros @Tool β†’ full AnyJSONTool conformance + JSON schema; @AgentActor β†’ boilerplate-free agent actors
Guardrails Input, output, tool-input, tool-output validators; tripwire and warning modes
Resilience Configurable retry backoff, circuit breaker, fallback agents, per-step timeouts
Observability SwiftLogTracer, OSLogTracer, span-based tracing, per-agent token metrics
MCP Model Context Protocol client + HTTP server
Providers Foundation Models (on-device), Anthropic, OpenAI, Ollama, Gemini, MLX via Conduit
Concurrency All public types Sendable; actors for all shared state; StrictConcurrency on every target

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Your Application                        β”‚
β”‚          iOS 26+ Β· macOS 26+ Β· Linux (Ubuntu 22.04+)        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚          AgentBlueprint Β· Runner.run() Β· .stream()           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   Orchestration DSL  β”‚  Step Types                          β”‚
β”‚   @resultBuilder     β”‚  Sequential Β· Parallel Β· DAGWorkflow β”‚
β”‚   --> Β· >>>          β”‚  Router Β· RepeatWhile Β· Branch       β”‚
β”‚   .dependsOn()       β”‚  Guard Β· Transform Β· HumanApproval   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚     Agents              Memory              Tools            β”‚
β”‚  Agent (tool-call)   Conversation        @Tool macro        β”‚
β”‚  ReActAgent          VectorMemory        FunctionTool       β”‚
β”‚  PlanAndExecute      SummaryMemory       AnyJSONTool ABI    β”‚
β”‚  HiveBackedAgent     HybridMemory        Runtime toggling   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚     Guardrails Β· Resilience Β· Observability Β· MCP           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                    Hive Runtime (HiveCore)                   β”‚
β”‚   Compiled DAG Β· Channel checkpointing Β· Tool approval      β”‚
β”‚   Deterministic retry Β· RetryPolicyBridge (jitter-free)     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚              InferenceProvider (pluggable)                   β”‚
β”‚   Foundation Models Β· Anthropic Β· OpenAI Β· Ollama Β· MLX     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

More Examples

Parallel Fan-Out

Run multiple agents on the same input concurrently. Each agent's metadata is automatically namespaced as "agentName.key" β€” no key collisions across concurrent results.

let group = ParallelGroup(
    agents: [
        (name: "primary", agent: primaryAgent),
        (name: "backup",  agent: backupAgent),
        (name: "expert",  agent: expertAgent),
    ],
    mergeStrategy: MergeStrategies.Concatenate(
        separator: "\n\n---\n\n",
        shouldIncludeAgentNames: true
    ),
    maxConcurrency: 3
)

let result = try await group.run("Analyze the Swift concurrency model.")

Semantic Memory (On-Device SIMD)

VectorMemory uses Accelerate's vDSP_dotpr for cosine similarity β€” no network call, no cloud embedding API.

let memory = VectorMemory(
    embeddingProvider: myEmbedder,
    similarityThreshold: 0.75,
    maxResults: 8
)

await memory.add(.user("The project deadline is March 15."))
await memory.add(.assistant("Noted. I will prioritise accordingly."))

// Semantically retrieves the deadline entry despite different phrasing
let context = await memory.context(for: "When is this due?", tokenLimit: 1_200)

Supervisor Routing

let supervisor = SupervisorAgent(
    agents: [
        (name: "math",    agent: mathAgent,    description: mathDesc),
        (name: "weather", agent: weatherAgent, description: weatherDesc),
        (name: "code",    agent: codeAgent,    description: codeDesc),
    ],
    routingStrategy: LLMRoutingStrategy(inferenceProvider: provider),
    fallbackAgent: Agent(instructions: "I am a general assistant.")
)

let result = try await supervisor.run("What is 15% of $240?")
// Routes to mathAgent

Resilience

let agent = FinanceAgent()
    .environment(\.inferenceProvider, provider)
    .withRetry(.exponentialBackoff(maxAttempts: 3, baseDelay: .seconds(1)))
    .withCircuitBreaker(threshold: 5, resetTimeout: .seconds(60))
    .withFallback(Agent(instructions: "Service temporarily unavailable."))
    .withTimeout(.seconds(30))

Streaming

for try await event in (fetchAgent --> writerAgent).stream("Summarise the changelog.") {
    switch event {
    case .outputToken(let token):  print(token, terminator: "")
    case .toolCalling(let call):   print("\n[tool: \(call.toolName)]")
    case .completed(let result):   print("\n\nDone in \(result.duration)")
    case .failed(let error):       print("\nError: \(error)")
    default: break
    }
}

Session Continuity

let session = InMemorySession(sessionId: "user-42")

try await agent.run("My portfolio has 100 AAPL shares.", session: session)
let result = try await agent.run("What is my total value?", session: session)
// Agent recalls the earlier context

Installation

Swift Package Manager

// Package.swift
dependencies: [
    .package(url: "https://github.com/christopherkarani/Swarm.git", from: "0.3.1")
],
targets: [
    .target(name: "YourApp", dependencies: ["Swarm"])
]

Xcode

File β†’ Add Package Dependencies β†’ https://github.com/christopherkarani/Swarm.git


Documentation

Topic Description
Agents Agent types, configuration, @AgentActor macro
Tools @Tool macro, FunctionTool, AnyJSONTool ABI, runtime toggling
Memory Conversation, Vector (SIMD), Summary, Hybrid, SwiftData backends
Sessions In-memory and persistent session management
Orchestration DAG, parallel, chains, handoffs, human-in-the-loop
Streaming AgentEvent streaming and SwiftUI integration
Observability Span-based tracing, OSLogTracer, SwiftLogTracer, metrics
Resilience Retry, circuit breakers, fallback agents, timeouts
Guardrails Input/output validation, tripwires, audit trails
MCP Model Context Protocol client and server
Providers Configuring inference providers

Requirements

Minimum
Swift 6.2
iOS 26.0
macOS 26.0
Linux Ubuntu 22.04 + Swift 6.2

iOS 26 / macOS 26 are required for Apple's on-device Foundation Models. External providers (Anthropic, OpenAI, Ollama) work on any Swift 6.2 platform including Linux.


Contributing

  1. Fork and create a branch: git checkout -b feature/my-feature
  2. All public types must be Sendable β€” StrictConcurrency will reject violations at build time
  3. Write tests first: swift test
  4. Format: swift package plugin --allow-writing-to-package-directory swiftformat
  5. Open a Pull Request β€” describe what changed and why it matters

See CONTRIBUTING.md for agent registry conventions and TDD workflow.


Support


License

MIT β€” see LICENSE for details.


Built in Swift for Apple platforms and Linux.

About

πŸ¦β€πŸ”₯ A Lightweight Agent Orchestration Framework written in pure Swift 6.2

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages