Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 38 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AnyLanguageModel

A Swift package that provides an API-compatible, drop-in replacement for
A Swift package that provides an API-compatible replacement for
[Apple's Foundation Models framework](https://developer.apple.com/documentation/FoundationModels)
with support for custom language model providers.

Expand Down Expand Up @@ -63,12 +63,47 @@ dependencies: [

## Usage

AnyLanguageModel is a drop-in replacement for Apple's Foundation Models framework.
All you need to do is change your import statement:

```diff
- import FoundationModels
+ import AnyLanguageModel
```

```swift
struct WeatherTool: Tool {
let name = "getWeather"
let description = "Retrieve the latest weather information for a city"

@Generable
struct Arguments {
@Guide(description: "The city to fetch the weather for")
var city: String
}

func call(arguments: Arguments) async throws -> String {
"The weather in \(arguments.city) is sunny and 72°F / 23°C"
}
}

let model = SystemLanguageModel.default
let session = LanguageModelSession(model: model, tools: [WeatherTool()])

let response = try await session.respond {
Prompt("How's the weather in Cupertino?")
}
print(response.content)
```

Here's an example using all of the available language model providers:

```swift
import AnyLanguageModel

// Core functionality (always available)
var models: [(any LanguageModel)] = [
SystemLanguageModel(), // Apple Foundation Models
SystemLanguageModel.default,
OllamaLanguageModel(model: "qwen3") // `ollama pull qwen3:0.6b`
AnthropicLanguageModel(
apiKey: ProcessInfo.processInfo.environment["ANTHROPIC_API_KEY"]!,
Expand All @@ -93,24 +128,9 @@ models.append(MLXLanguageModel(modelId: "mlx-community/Qwen3-0.6B-4bit"))
models.append(LlamaLanguageModel(modelPath: "/path/to/model.gguf"))
#endif

struct WeatherTool: Tool {
let name = "getWeather"
let description = "Retrieve the latest weather information for a city"

@Generable
struct Arguments {
@Guide(description: "The city to fetch the weather for")
var city: String
}

func call(arguments: Arguments) async throws -> String {
"The weather in \(arguments.city) is sunny and 72°F / 23°C"
}
}

for model in models {
let session = LanguageModelSession(model: model, tools: [WeatherTool()])
let response = try await session.respond(to: "What's the weather in Cupertino?")
let response = try await session.respond(to: "How's the weather in Cupertino?")
print(response.text) // "It's sunny and 72°F in Cupertino"
}
```
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Testing

#if canImport(FoundationModels)
import AnyLanguageModel

@available(macOS 26.0, *)
@Test("AnyLanguageModel Drop-In Compatibility", .enabled(if: SystemLanguageModel.default.isAvailable))
func anyLanguageModelCompatibility() async throws {
let model = SystemLanguageModel.default
let session = LanguageModelSession(
model: model,
instructions: Instructions("You are a helpful assistant.")
)

let options = GenerationOptions(temperature: 0.7)
let response = try await session.respond(options: options) {
Prompt("Say 'Hello'")
}
#expect(!response.content.isEmpty)

let stream = session.streamResponse {
Prompt("Count to 3")
}
var hasSnapshots = false
for try await _ in stream {
hasSnapshots = true
break
}
#expect(hasSnapshots)
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Testing

#if canImport(FoundationModels)
import FoundationModels

@available(macOS 26.0, *)
@Test("FoundationModels Drop-In Compatibility", .enabled(if: SystemLanguageModel.default.isAvailable))
func foundationModelsCompatibility() async throws {
let model = SystemLanguageModel.default
let session = LanguageModelSession(
model: model,
instructions: Instructions("You are a helpful assistant.")
)

let options = GenerationOptions(temperature: 0.7)
let response = try await session.respond(options: options) {
Prompt("Say 'Hello'")
}
#expect(!response.content.isEmpty)

let stream = session.streamResponse {
Prompt("Count to 3")
}
var hasSnapshots = false
for try await _ in stream {
hasSnapshots = true
break
}
#expect(hasSnapshots)
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ struct AnthropicLanguageModelTests {
let weatherTool = WeatherTool()
let session = LanguageModelSession(model: model, tools: [weatherTool])

let response = try await session.respond(to: "What's the weather in San Francisco?")
let response = try await session.respond(to: "How's the weather in San Francisco?")

var foundToolOutput = false
for case let .toolOutput(toolOutput) in response.transcriptEntries {
Expand Down
2 changes: 1 addition & 1 deletion Tests/AnyLanguageModelTests/MLXLanguageModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ import Testing
instructions: "You are a helpful assistant. Use available tools when needed."
)

let response = try await session.respond(to: "What's the weather in San Francisco?")
let response = try await session.respond(to: "How's the weather in San Francisco?")

var foundToolOutput = false
for case let .toolOutput(toolOutput) in response.transcriptEntries {
Expand Down
2 changes: 1 addition & 1 deletion Tests/AnyLanguageModelTests/OllamaLanguageModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ struct OllamaLanguageModelTests {
let weatherTool = spy(on: WeatherTool())
let session = LanguageModelSession(model: model, tools: [weatherTool])

let response = try await session.respond(to: "What's the weather in San Francisco?")
let response = try await session.respond(to: "How's the weather in San Francisco?")

var foundToolOutput = false
for case let .toolOutput(toolOutput) in response.transcriptEntries {
Expand Down
4 changes: 2 additions & 2 deletions Tests/AnyLanguageModelTests/OpenAILanguageModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ struct OpenAILanguageModelTests {
let weatherTool = WeatherTool()
let session = LanguageModelSession(model: model, tools: [weatherTool])

let response = try await session.respond(to: "What's the weather in San Francisco?")
let response = try await session.respond(to: "How's the weather in San Francisco?")

var foundToolOutput = false
for case let .toolOutput(toolOutput) in response.transcriptEntries {
Expand Down Expand Up @@ -196,7 +196,7 @@ struct OpenAILanguageModelTests {
let weatherTool = WeatherTool()
let session = LanguageModelSession(model: model, tools: [weatherTool])

let response = try await session.respond(to: "What's the weather in San Francisco?")
let response = try await session.respond(to: "How's the weather in San Francisco?")

var foundToolOutput = false
for case let .toolOutput(toolOutput) in response.transcriptEntries {
Expand Down