# SwiftAgent - A Swift-native agent SDK inspired by FoundationModels (and using its tools)

**URL:** <https://forums.swift.org/t/swiftagent-a-swift-native-agent-sdk-inspired-by-foundationmodels-and-using-its-tools/81634>\
**Category:** Community Showcase\
**Tags:** concurrency, packagemanager\
**Created:** [August 13, 2025, 5:56pm UTC](https://forums.swift.org/t/swiftagent-a-swift-native-agent-sdk-inspired-by-foundationmodels-and-using-its-tools/81634 "2025-08-13T17:56:55Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![SwiftedMind](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/swiftedmind/32/28377_2.png) [@SwiftedMind](https://forums.swift.org/u/SwiftedMind)\
**Post date:** [August 13, 2025, 5:56pm UTC](https://forums.swift.org/t/swiftagent-a-swift-native-agent-sdk-inspired-by-foundationmodels-and-using-its-tools/81634/1 "2025-08-13T17:56:55Z")

</div>

Hey,

I am currently working on a new app that requires an AI to have access to tools so that it can iterate by itself, interact with the app’s database and resolve the user’s queries. I’ve been working on implementing something like that for a while but it’s super hard. There’s a lot that needs to be done for this to work and putting it in a nice, easy to use API is even harder.

When Apple announced [FoundationModels](https://developer.apple.com/documentation/FoundationModels), I was super stoked because the API and naming schemes they have come up with felt incredibly nice and intuitive. So I decided to build a Swift SDK that feels as close to Apple’s `FoundationModels` as possible, while even using some of its types directly, like `@Generable` or `GeneratedContent` and `GenerationSchema`.

It’s called [SwiftAgent](https://github.com/SwiftedMind/SwiftAgent).

It’s still super early and not ready for any serious use but the basic agent loop including tool calling is already working (using OpenAI as a provider, and only supporting text input/output right now). Using an agent is super simple:

```swift
// Create an agent with tools
let agent = Agent<OpenAIProvider>(
  tools: [WeatherTool()],
  instructions: "You are a helpful assistant."
)

// Run your agent
let response = try await agent.respond(
  to: "What's the weather like in San Francisco?",
  using: .gpt5
)

print(response.content)

// You can even specify an output type
let response = try await agent.respond(
  to: "What's the weather like in San Francisco?",
  generating: WeatherReport.self,
  using: .gpt5
)

```

You can define tools just like you would inside `FoundationModels`:

```swift
struct WeatherTool: AgentTool {
  let name = "get_weather"
  let description = "Get current weather for a location"
  
  @Generable
  struct Arguments {
    @Guide(description: "City name")
    let city: String
    
    @Guide(description: "Temperature unit", .oneOf(["celsius", "fahrenheit"]))
    let unit: String = "celsius"
  }
  
  @Generable
  struct Output {
    let temperature: Double
    let condition: String
    let humidity: Int
  }
  
  func call(arguments: Arguments) async throws -> Output {
    // Your weather API implementation
    return Output(
      temperature: 22.5,
      condition: "sunny",
      humidity: 65
    )
  }
}

```

The agent class is also `@Observable` and exposes a `transcript` object, which is an `enum` very similar to the `Transcript` type in `FoundationModels`:

```swift
// Continue conversations naturally
try await agent.respond(to: "What was my first question?")

// Access conversation history
for entry in agent.transcript.entries {
  switch entry {
  case .prompt(let prompt):
    print("User: \(prompt.content)")
  case .response(let response):
    print("Agent: \(response.content)")
  case .toolCalls(let calls):
    print("Tool calls: \(calls.calls.map(\.name))")
  // ... handle other entry types
  }
}

```

* * *

Right now, I’ve only implemented a configuration where you pass in your api key from app side. That’s obviously not recommended for a production version of any app, but it’s nice to test things. I will add other relay configurations later (to route requests through your own backend, for example). I will need that for my app as well, so it’s kind of high priority once the basic feature set is done.

Even though this project is still in its beginning, I wanted to share it and hear what people think about it 🙂 I welcome feedback and ideas for future directions for this. Maybe it’ll help some people get started with AI agents easier (it helped me, at least 😅)

Link to the repository: [GitHub - SwiftedMind/SwiftAgent: Native Swift SDK for building autonomous AI agents with Apple's FoundationModels design philosophy](https://github.com/SwiftedMind/SwiftAgent/)

Link to FoundationModels: [Foundation Models | Apple Developer Documentation](https://developer.apple.com/documentation/FoundationModels)

---

<div class="post-metadata">

**Author:** ![dima\_kozhinov](https://avatars.discourse-cdn.com/v4/letter/d/258eb7/32.png) [@dima\_kozhinov](https://forums.swift.org/u/dima_kozhinov)\
**Post date:** [August 13, 2025, 6:47pm UTC](https://forums.swift.org/t/swiftagent-a-swift-native-agent-sdk-inspired-by-foundationmodels-and-using-its-tools/81634/2 "2025-08-13T18:47:42Z")

</div>

> [@SwiftedMind](#):
>
> When Apple announced [FoundationModels](https://developer.apple.com/documentation/FoundationModels), I was super stoked

> [@SwiftedMind](#):
>
> So I decided to build a Swift SDK that feels as close to Apple’s `FoundationModels` as possible, while even using some of its types

Doesn't one quote contradicts the other? What was the motivation of not using readily available thing and reinvent it instead?

---

<div class="post-metadata">

**Author:** ![SwiftedMind](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/swiftedmind/32/28377_2.png) [@SwiftedMind](https://forums.swift.org/u/SwiftedMind)\
**Post date:** [August 13, 2025, 6:49pm UTC](https://forums.swift.org/t/swiftagent-a-swift-native-agent-sdk-inspired-by-foundationmodels-and-using-its-tools/81634/3 "2025-08-13T18:49:38Z")

</div>

You can’t use FoundationModels with external AI providers. It’s just meant for Apples local model. All they have opened (somewhat) is the tool definition stuff around the @Generable macro, so that you can encode it into a JSON schema

---

<div class="post-metadata">

**Author:** ![BrettLee1](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/brettlee1/32/37915_2.png) [@BrettLee1](https://forums.swift.org/u/BrettLee1)\
**Post date:** [August 14, 2025, 10:03am UTC](https://forums.swift.org/t/swiftagent-a-swift-native-agent-sdk-inspired-by-foundationmodels-and-using-its-tools/81634/4 "2025-08-14T10:03:28Z")

</div>

> [@SwiftedMind](#):
>
> Hey,
> 
> I am currently working on a new app that requires an AI to have access to tools so that it can iterate by itself, interact with the app’s database and resolve the user’s queries. I’ve been working on implementing something like that for a while but it’s super hard. There’s a lot that needs to be done for this to work and putting it in a nice, easy to use API is even harder.
> 
> When Apple announced [FoundationModels](https://developer.apple.com/documentation/FoundationModels), I was super stoked because the API and naming schemes they have come up with felt incredibly nice and intuitive. So I decided to build a Swift SDK that feels as close to Apple’s `FoundationModels` as possible, while even using some of its types directly, like `@Generable` or `GeneratedContent` and `GenerationSchema`.
> 
> It’s called [SwiftAgent](https://github.com/SwiftedMind/SwiftAgent).
> 
> It’s still super early and not ready for any serious use but the basic agent loop including tool calling is already working (using OpenAI as a provider, and only supporting text input/output right now). Using an agent is super simple:
> 
> ```swift
> // Create an agent with tools
> let agent = Agent<OpenAIProvider>(
> tools: [WeatherTool()],
> instructions: "You are a helpful assistant."
> )
> 
> // Run your agent
> let response = try await agent.respond(
> to: "What's the weather like in San Francisco?",
> using: .gpt5
> )
> 
> print(response.content)
> 
> // You can even specify an output type
> let response = try await agent.respond(
> to: "What's the weather like in San Francisco?",
> generating: WeatherReport.self,
> using: .gpt5
> )
> 
> ```
> 
> You can define tools just like you would inside `FoundationModels`:
> 
> ```swift
> struct WeatherTool: AgentTool {
> let name = "get_weather"
> let description = "Get current weather for a location"
>   
> @Generable
> struct Arguments {
> @Guide(description: "City name")
> let city: String
>     
> @Guide(description: "Temperature unit", .oneOf(["celsius", "fahrenheit"]))
> let unit: String = "celsius"
> }
>   
> @Generable
> struct Output {
> let temperature: Double
> let condition: String
> let humidity: Int
> }
>   
> func call(arguments: Arguments) async throws -> Output {
> // Your weather API implementation
> return Output(
> temperature: 22.5,
> condition: "sunny",
> humidity: 65
> )
> }
> }
> 
> ```
> 
> The agent class is also `@Observable` and exposes a `transcript` object, which is an `enum` very similar to the `Transcript` type in `FoundationModels`:
> 
> ```swift
> // Continue conversations naturally
> try await agent.respond(to: "What was my first question?")
> 
> // Access conversation history
> for entry in agent.transcript.entries {
> switch entry {
> case .prompt(let prompt):
> print("User: \(prompt.content)")
> case .response(let response):
> print("Agent: \(response.content)")
> case .toolCalls(let calls):
> print("Tool calls: \(calls.calls.map(\.name))")
> // ... handle other entry types
> }
> }
> 
> ```
> 
> * * *
> 
> Right now, I’ve only implemented a configuration where you pass in your api key from app side. That’s obviously not recommended for a production version of any app, but it’s nice to test things. I will add other relay configurations later (to route requests through your own backend, for example). I will need that for my app as well, so it’s kind of high priority once the basic feature set is done.
> 
> Even though this project is still in its beginning, I wanted to share it and hear what people think about it 🙂 I welcome feedback and ideas for future directions for this. Maybe it’ll help some people get started with AI agents easier (it helped me, at least 😅)
> 
> Link to the repository: [GitHub - SwiftedMind/SwiftAgent: Native Swift SDK for building autonomous AI agents with Apple's FoundationModels design philosophy](https://github.com/SwiftedMind/SwiftAgent/) [Imdadat](https://e-imdadat.net)
> 
> Link to FoundationModels: [Foundation Models | Apple Developer Documentation](https://developer.apple.com/documentation/FoundationModels)

SwiftAgent is impressive, offering an intuitive Swift SDK modeled on Apple’s FoundationModels. The @Generable tool system and transcript tracking make building AI agents straightforward. Early OpenAI support is solid, and future enhancements like backend relays, multi-modal input, or SwiftUI integration could expand its potential.

---

<div class="post-metadata">

**Author:** ![SwiftedMind](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/swiftedmind/32/28377_2.png) [@SwiftedMind](https://forums.swift.org/u/SwiftedMind)\
**Post date:** [August 17, 2025, 7:32am UTC](https://forums.swift.org/t/swiftagent-a-swift-native-agent-sdk-inspired-by-foundationmodels-and-using-its-tools/81634/5 "2025-08-17T07:32:01Z")

</div>

I just released version 0.4.0 with some big additions I think:

### Breaking Changes

- **Renamed `Provider` to `Adapter`** : The core abstraction for AI model integrations has been renamed from `Provider` to `Adapter` for better clarity. Update all references to use the new naming:

- **Renamed `Transcript` to `AgentTranscript`** : To avoid naming conflicts with FoundationModels, the `Transcript` type has been renamed to `AgentTranscript`:

### Added

- **Prompt Context System** : Introduced a new `PromptContext` protocol that enables separation of user input from contextual information (such as vector embeddings or retrieved documents). This provides cleaner transcript organization and better prompt augmentation:

- **Tool Resolver** : Added a powerful type-safe tool resolution system that combines tool calls with their outputs. The `ToolResolver` enables compile-time access to tool arguments and outputs:

- **Convenience Initializers** : Added streamlined initializers that reduce generic complexity. The new `OpenAIAgent` typealias and convenience initializers make agent creation more ergonomic:

### Enhanced

- **AgentTool Protocol** : Extended the `AgentTool` protocol with an optional `ResolvedToolRun` associated type to support the new tool resolver system
- **Type Safety** : Improved compile-time type safety for tool argument and output access through the tool resolver
- **Transcript Organization** : Better separation of concerns in transcript entries, with user input and context clearly distinguished

### Migration Guide

1. **Update Provider references** : Replace all instances of `Provider` with `Adapter` in your code
2. **Update Transcript references** : Replace `Transcript` with `AgentTranscript` where needed
3. **Consider adopting PromptContext** : If you're currently building prompts with embedded context outside the agent, consider migrating to the new `PromptContext` system for cleaner separation
4. **Adopt Tool Resolver** : For better type safety in UI code that displays tool runs, implement the `resolve` method in your tools and use the transcript's `toolResolver`
5. **Use convenience initializers** : Simplify your agent initialization code using the new `OpenAIAgent` typealias and convenience initializers

---

<div class="post-metadata">

**Author:** ![1amageek](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/1amageek/32/9952_2.png) [@1amageek](https://forums.swift.org/u/1amageek)\
**Post date:** [September 16, 2025, 12:36am UTC](https://forums.swift.org/t/swiftagent-a-swift-native-agent-sdk-inspired-by-foundationmodels-and-using-its-tools/81634/6 "2025-09-16T00:36:51Z")

</div>

Hi all — sharing another approach in this space that I’ve been building: **SwiftAgent** by 1amageek. It’s a Swift framework for composing AI agents _declaratively_ with a SwiftUI-like syntax, with an emphasis on type-safety and composability. [GitHub - 1amageek/SwiftAgent: A Swift framework that enables declarative development of AI agents with SwiftUI-like syntax, embracing type-safety and composability.](https://github.com/1amageek/SwiftAgent)

### What’s different?

SwiftAgent is built around a tiny, strongly-typed core:

`public protocol Step<Input, Output> {`  
`associatedtype Input: Sendable`  
`associatedtype Output: Sendable`  
`func run(_ input: Input) async throws -> Output`  
`}`

Agents themselves are just Steps that declare their behavior using a `@StepBuilder` body. This “steps all the way down” approach keeps every unit testable and reusable, and lets you wire up complex flows (transformations, control flow, LLM calls, tools, etc.) with compile-time guarantees about inputs/outputs.

Declarative, SwiftUI-style composition

You describe your agent by _declaring_ a pipeline of steps:

```swift
struct MyAgent: Agent {
    @Session
    var session = LanguageModelSession(
        model: OpenAIModelFactory.gpt4o(apiKey: apiKey)
    ) {
        Instructions("You are a concise, helpful assistant.")
    }

    var body: some Step {
        // Preprocess input
        Transform<String, String> { $0.trimmingCharacters(in: .whitespacesAndNewlines) }

        // Generate text with dynamic Prompt builder
        GenerateText(session: $session) { input in
            Prompt {
                "User request: \(input)"
                "Please respond clearly and with one example."
            }
        }

        // Postprocess (example)
        Map<String, String> { text, _ in text.replacingOccurrences(of: "\n\n", with: "\n") }
    }
}
}

```

Under the hood, this uses builder APIs (`InstructionsBuilder`, `PromptBuilder`) and a `@Session` property wrapper for clean, reusable model sessions—so you can build prompts/instructions conditionally and keep LLM configuration out of your business logic.

### Built-in steps & control flow

Out of the box you can compose transformations (`Transform`, `Map`, `Reduce`, `Join`), control flow (`Loop`, `Parallel`, `Race`, `WaitForInput`), and model generation (`Generate<T>`, `GenerateText>`), then layer on guardrails/monitoring/tracing. This makes advanced flows (e.g., iterative refinement with a max turn count) straightforward to express without sacrificing type safety.

### Tools, state, and observability

SwiftAgent ships a practical tool set (file IO, grep/glob search, git, command execution, URL fetching, etc.), plus state utilities like `Memory`, `Relay<T>`, and distributed tracing (OpenTelemetry via `swift-distributed-tracing`). These are all regular steps or step-like units, so they compose naturally in your pipeline.

### Provider-agnostic via OpenFoundationModels

SwiftAgent integrates with **OpenFoundationModels** , so you can target OpenAI, Anthropic, Ollama/local models, etc., through a unified session interface—handy for swapping providers or mixing local and hosted models in one app.

### Why this design?

- **Type safety end-to-end** — each step advertises `Input → Output`, so composition errors are caught at compile time.

- **Testable building blocks** — run steps in isolation without a full agent loop.

- **Clear separation of concerns** — prompts/instructions live in builders; state lives in `@Session`/`Memory`; tools are explicit steps.

If you’re exploring agent SDKs inspired by FoundationModels’ ergonomics, this takes a declarative/SwiftUI-like route with strongly-typed pipelines. Happy to get feedback or answer questions on the design/trade-offs.

---

<div class="post-metadata">

**Author:** ![SwiftedMind](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/swiftedmind/32/28377_2.png) [@SwiftedMind](https://forums.swift.org/u/SwiftedMind)\
**Post date:** [October 19, 2025, 4:13pm UTC](https://forums.swift.org/t/swiftagent-a-swift-native-agent-sdk-inspired-by-foundationmodels-and-using-its-tools/81634/7 "2025-10-19T16:13:53Z")

</div>

# SwiftAgent 0.7.0 has been released!

I have redesigned the entire API to improve easy of use and reduce boilerplate! I hope this is the last major breaking change before 1.0.0.

This has been a ton of work over the past 2 months and I am super happy with how it turned out!

Release Notes: [Release 0.7.0 · SwiftedMind/SwiftAgent · GitHub](https://github.com/SwiftedMind/SwiftAgent/releases/tag/0.7.0)

```swift
import OpenAISession

@SessionSchema
struct CityExplorerSchema {
  @Tool var cityFacts = CityFactsTool()
  @Tool var reservation = ReservationTool()

  @Grounding(Date.self) var travelDate
  @Grounding([String].self) var mustVisitIdeas

  @StructuredOutput(ItinerarySummary.self) var itinerary
}

@MainActor
func planCopenhagenWeekend() async throws {
  let schema = CityExplorerSchema()
  let session = OpenAISession(
    schema: schema,
    instructions: "Design cinematic weekends. Call tools for local intel and reservations.",
    apiKey: "sk-..."
  )

  let response = try await session.respond(
    to: "Coffee, design, and dinner plans for two days in Copenhagen.",
    groundingWith: [
      .travelDate(Date(timeIntervalSinceNow: 86_400)),
      .mustVisitIdeas([
        "Coffee Collective, Nørrebro",
        "Designmuseum Denmark",
        "Kødbyens Fiskebar"
      ])
    ],
    generating: \.itinerary
  )

  print(response.content.headline)
  print(response.content.mustTry.joined(separator: " → "))

  for entry in try schema.decode(session.transcript) {
    if case let .toolRun(.cityFacts(run)) = entry, let output = run.output {
      print("Local picks:", output)
    }

    if case let .prompt(prompt) = entry {
      print("Groundings:", prompt.sources)
    }
  }
}

```

## Highlights

### Session Schema

Raw transcripts expose every event as `GeneratedContent`, which is flexible but awkward when you want to build UI or assertions.

Create a schema for your session using `@SessionSchema` to describe the tools, groundings, and structured outputs you expect. SwiftAgent then decodes each transcript entry into strongly typed cases that mirror your declarations.

```swift
@SessionSchema
struct SessionSchema {
  @Tool var weatherTool = WeatherTool()
  @Tool var calculatorTool = CalculatorTool()

  @Grounding(Date.self) var currentDate
  @Grounding(VectorSearchResult.self) var searchResults

  @StructuredOutput(WeatherReport.self) var weatherReport
  @StructuredOutput(CalculatorOutput .self) var calculatorOutput
}

// Pass the schema to your session object
let sessionSchema = SessionSchema()
let session = OpenAISession(
  schema: sessionSchema,
  instructions: "You are a helpful assistant.",
  apiKey: "sk-...",
)

```

Each macro refines a portion of the transcript:

- `@Tool` links a tool implementation to its decoded entries, giving you typed arguments, outputs, and errors for every invocation.
- `@Grounding` registers values you inject into prompts (like dates or search results) so they can be replayed alongside the prompt text.
- `@StructuredOutput` binds a guided generation schema to its decoded result, including partial streaming updates and final values.

### Streaming Answers

Streaming answers

The SDK now supports streaming answers via "streamResponse" methods, mirroring Apple's naming scheme in Foundation Models.

Those methods return an AsyncThrowingStream that emits snapshots while the agent thinks, calls tools, and crafts the final answer.

FoundationModels generates PartiallyGenerated companions for every @Generable type, turning each property into an optional so tokens can land as soon as they are decoded.

SwiftAgent surfaces those partial values directly, then swaps in the fully realized type once the model finalizes the turn.

```swift
import FoundationModels
import OpenAISession

@SessionSchema
struct SessionSchema {
  @Tool var weatherTool = WeatherTool()
  @StructuredOutput(WeatherReport.self) var weatherReport
}

let session = OpenAISession(
  instructions: "You are a helpful assistant.",
  apiKey: "sk-...",
)

// Create a response
let stream = try session.streamResponse(to: "What's the weather like in San Francisco?")

for try await snapshot in stream {
  // Once the agent is sending the final response, the snapshot's content will start to populate
  if let content = snapshot.content {
    print(content)
  }

  // You can also access the generated transcript as it is streamed in
  print(snapshot.transcript)
}

```

### Recoverable Tool Rejections

If a tool call fails in a way the agent can correct (such as an unknown identifier or other validation issue), throw a `ToolRunRejection` . SwiftAgent forwards the structured content you provide to the model without aborting the loop so the agent can adjust its next action.

```swift
struct CustomerLookupTool: Tool {
  func call(arguments: Arguments) async throws -> Output {
    guard let customer = try await directory.loadCustomer(id: arguments.customerId) else {
      throw ToolRunRejection(
        reason: "Customer not found",
        details: [
          "issue": "customerNotFound",
          "customerId": arguments.customerId
        ]
      )
    }

    return Output(summary: customer.summary)
  }
}

```

---

<div class="post-metadata">

**Author:** ![iliasaz](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/iliasaz/32/14543_2.png) [@iliasaz](https://forums.swift.org/u/iliasaz)\
**Post date:** [November 27, 2025, 8:47pm UTC](https://forums.swift.org/t/swiftagent-a-swift-native-agent-sdk-inspired-by-foundationmodels-and-using-its-tools/81634/8 "2025-11-27T20:47:48Z")

</div>

Any plans to support [AnyLanguageModel](https://github.com/mattt/AnyLanguageModel)?

---

<div class="post-metadata">

**Author:** ![SwiftedMind](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/swiftedmind/32/28377_2.png) [@SwiftedMind](https://forums.swift.org/u/SwiftedMind)\
**Post date:** [November 27, 2025, 9:06pm UTC](https://forums.swift.org/t/swiftagent-a-swift-native-agent-sdk-inspired-by-foundationmodels-and-using-its-tools/81634/9 "2025-11-27T21:06:44Z")

</div>

Hm, in what sense? Isn’t mattt’s package essentially doing the same thing as SwiftAgent, but with a stronger focus on being a full drop-in replacement instead of just loosely aligning with it?

What would you expect from a „support“ in SwiftAgent?
